From deaf8b167690ce7077f38eaf7c16ae20aa4992d2 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Tue, 20 Dec 2022 09:00:43 -0800 Subject: [PATCH 1/7] Support for gradients as backgrounds & other optimizations. --- core/Cargo.toml | 3 + core/src/angle.rs | 15 + core/src/background.rs | 14 +- core/src/gradient.rs | 253 ++++++++++----- core/src/gradient/linear.rs | 112 ------- core/src/lib.rs | 2 + core/src/renderer.rs | 2 +- examples/modern_art/Cargo.toml | 11 - examples/modern_art/src/main.rs | 143 --------- examples/solar_system/src/main.rs | 20 +- examples/tour/src/main.rs | 51 ++- glow/src/program.rs | 0 glow/src/quad/compatibility.rs | 0 glow/src/quad/core.rs | 0 glow/src/shader/includes/border_radius.vert | 9 + glow/src/shader/includes/gradient.frag | 61 ++++ glow/src/shader/includes/quad_distance.vert | 17 + .../shader/quad/compatibility/gradient.frag | 64 ++++ .../shader/quad/compatibility/gradient.vert | 72 +++++ glow/src/shader/quad/compatibility/solid.frag | 56 ++++ glow/src/shader/quad/compatibility/solid.vert | 45 +++ glow/src/shader/quad/core/gradient.frag | 69 +++++ glow/src/shader/quad/core/gradient.vert | 80 +++++ glow/src/shader/quad/core/solid.frag | 67 ++++ glow/src/shader/quad/core/solid.vert | 52 ++++ glow/src/shader/triangle/gradient.frag | 23 ++ glow/src/shader/triangle/gradient.vert | 42 +++ glow/src/shader/triangle/solid.vert | 11 + glow/src/window/compositor.rs | 0 graphics/src/primitive.rs | 7 +- style/src/button.rs | 3 + style/src/theme.rs | 3 + wgpu/src/buffer.rs | 254 ++++++++++++++- wgpu/src/buffer/dynamic.rs | 202 ------------ wgpu/src/geometry.rs | 66 ++-- wgpu/src/layer.rs | 60 +++- wgpu/src/layer/mesh.rs | 5 +- wgpu/src/layer/quad.rs | 51 ++- wgpu/src/quad.rs | 2 - wgpu/src/shader/gradient.wgsl | 88 ------ wgpu/src/shader/quad.wgsl | 292 +++++++++++++++--- wgpu/src/shader/triangle/gradient.wgsl | 139 +++++++++ wgpu/src/shader/{ => triangle}/solid.wgsl | 10 +- wgpu/src/triangle.rs | 202 ++++-------- 44 files changed, 1764 insertions(+), 914 deletions(-) create mode 100644 core/src/angle.rs delete mode 100644 examples/modern_art/Cargo.toml create mode 100644 glow/src/program.rs create mode 100644 glow/src/quad/compatibility.rs create mode 100644 glow/src/quad/core.rs create mode 100644 glow/src/shader/includes/border_radius.vert create mode 100644 glow/src/shader/includes/gradient.frag create mode 100644 glow/src/shader/includes/quad_distance.vert create mode 100644 glow/src/shader/quad/compatibility/gradient.frag create mode 100644 glow/src/shader/quad/compatibility/gradient.vert create mode 100644 glow/src/shader/quad/compatibility/solid.frag create mode 100644 glow/src/shader/quad/compatibility/solid.vert create mode 100644 glow/src/shader/quad/core/gradient.frag create mode 100644 glow/src/shader/quad/core/gradient.vert create mode 100644 glow/src/shader/quad/core/solid.frag create mode 100644 glow/src/shader/quad/core/solid.vert create mode 100644 glow/src/shader/triangle/gradient.frag create mode 100644 glow/src/shader/triangle/gradient.vert create mode 100644 glow/src/shader/triangle/solid.vert create mode 100644 glow/src/window/compositor.rs delete mode 100644 wgpu/src/shader/gradient.wgsl create mode 100644 wgpu/src/shader/triangle/gradient.wgsl rename wgpu/src/shader/{ => triangle}/solid.wgsl (67%) diff --git a/core/Cargo.toml b/core/Cargo.toml index dac3182868..45c450b3d2 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,5 +16,8 @@ twox-hash = { version = "1.5", default-features = false } version = "0.6" optional = true +[dependencies.thiserror] +version = "1.0.37" + [target.'cfg(target_arch = "wasm32")'.dependencies] instant = "0.1" diff --git a/core/src/angle.rs b/core/src/angle.rs new file mode 100644 index 0000000000..167cc5ed3d --- /dev/null +++ b/core/src/angle.rs @@ -0,0 +1,15 @@ +use std::f32::consts::PI; + +#[derive(Debug, Copy, Clone, PartialEq)] +/// Degrees +pub struct Degrees(pub f32); + +#[derive(Debug, Copy, Clone, PartialEq)] +/// Radians +pub struct Radians(pub f32); + +impl From for Radians { + fn from(degrees: Degrees) -> Self { + Radians(degrees.0 * (PI / 180.0)) + } +} diff --git a/core/src/background.rs b/core/src/background.rs index cfb958674a..92107d859a 100644 --- a/core/src/background.rs +++ b/core/src/background.rs @@ -1,11 +1,13 @@ -use crate::Color; +use crate::{Color, Gradient}; /// The background of some element. #[derive(Debug, Clone, Copy, PartialEq)] pub enum Background { - /// A solid color + /// A solid color. Color(Color), - // TODO: Add gradient and image variants + /// Interpolate between several colors. + Gradient(Gradient), + // TODO: Add image variant } impl From for Background { @@ -19,3 +21,9 @@ impl From for Option { Some(Background::from(color)) } } + +impl From for Option { + fn from(gradient: Gradient) -> Self { + Some(Background::Gradient(gradient)) + } +} diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 61e919d6bf..503d757ae5 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -1,27 +1,96 @@ //! For creating a Gradient. -pub mod linear; - pub use linear::Linear; -use crate::{Color, Point, Size}; +use crate::{Color, Radians, Rectangle, Vector}; -#[derive(Debug, Clone, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq)] /// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), /// or conically (TBD). +/// +/// For a gradient which can be used as a fill on a canvas, see [`iced_graphics::Gradient`]. pub enum Gradient { - /// A linear gradient interpolates colors along a direction from its `start` to its `end` - /// point. + /// A linear gradient interpolates colors along a direction at a specific [`Angle`]. Linear(Linear), } impl Gradient { /// Creates a new linear [`linear::Builder`]. - pub fn linear(position: impl Into) -> linear::Builder { - linear::Builder::new(position.into()) + /// + /// This must be defined by an angle (in [`Degrees`] or [`Radians`]) + /// which will use the bounds of the widget as a guide. + pub fn linear(angle: impl Into) -> linear::Builder { + linear::Builder::new(angle.into()) + } + + /// Adjust the opacity of the gradient by a multiplier applied to each color stop. + pub fn adjust_alpha(mut self, alpha_multiplier: f32) -> Self { + match &mut self { + Gradient::Linear(linear) => { + for stop in &mut linear.color_stops { + stop.color.a *= alpha_multiplier; + } + } + } + + self + } + + /// Packs the [`Gradient`] into a buffer for use in shader code. + pub fn flatten(&self, bounds: Rectangle) -> [f32; 44] { + match self { + Gradient::Linear(linear) => { + let mut pack: [f32; 44] = [0.0; 44]; + let mut offsets: [f32; 8] = [2.0; 8]; + + for (index, stop) in + linear.color_stops.iter().enumerate().take(8) + { + let [r, g, b, a] = stop.color.into_linear(); + + pack[(index * 4)] = r; + pack[(index * 4) + 1] = g; + pack[(index * 4) + 2] = b; + pack[(index * 4) + 3] = a; + + offsets[index] = stop.offset; + } + + pack[32] = offsets[0]; + pack[33] = offsets[1]; + pack[34] = offsets[2]; + pack[35] = offsets[3]; + pack[36] = offsets[4]; + pack[37] = offsets[5]; + pack[38] = offsets[6]; + pack[39] = offsets[7]; + + let v1 = Vector::new( + f32::cos(linear.angle.0), + f32::sin(linear.angle.0), + ); + + let distance_to_rect = f32::min( + f32::abs((bounds.y - bounds.center().y) / v1.y), + f32::abs( + ((bounds.x + bounds.width) - bounds.center().x) / v1.x, + ), + ); + + let start = bounds.center() + v1 * distance_to_rect; + let end = bounds.center() - v1 * distance_to_rect; + + pack[40] = start.x; + pack[41] = start.y; + pack[42] = end.x; + pack[43] = end.y; + + pack + } + } } } -#[derive(Debug, Clone, Copy, PartialEq)] +#[derive(Debug, Default, Clone, Copy, PartialEq)] /// A point along the gradient vector where the specified [`color`] is unmixed. /// /// [`color`]: Self::color @@ -35,83 +104,109 @@ pub struct ColorStop { pub color: Color, } -#[derive(Debug)] -/// The position of the gradient within its bounds. -pub enum Position { - /// The gradient will be positioned with respect to two points. - Absolute { - /// The starting point of the gradient. - start: Point, - /// The ending point of the gradient. - end: Point, - }, - /// The gradient will be positioned relative to the provided bounds. - Relative { - /// The top left position of the bounds. - top_left: Point, - /// The width & height of the bounds. - size: Size, - /// The start [Location] of the gradient. - start: Location, - /// The end [Location] of the gradient. - end: Location, - }, -} +pub mod linear { + //! Linear gradient builder & definition. + use crate::gradient::{ColorStop, Gradient}; + use crate::{Color, Radians}; -impl From<(Point, Point)> for Position { - fn from((start, end): (Point, Point)) -> Self { - Self::Absolute { start, end } + /// A linear gradient that can be used as a [`Background`]. + #[derive(Debug, Clone, Copy, PartialEq)] + pub struct Linear { + /// How the [`Gradient`] is angled within its bounds. + pub angle: Radians, + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: [ColorStop; 8], } -} -#[derive(Debug, Clone, Copy)] -/// The location of a relatively-positioned gradient. -pub enum Location { - /// Top left. - TopLeft, - /// Top. - Top, - /// Top right. - TopRight, - /// Right. - Right, - /// Bottom right. - BottomRight, - /// Bottom. - Bottom, - /// Bottom left. - BottomLeft, - /// Left. - Left, -} + /// A [`Linear`] builder. + #[derive(Debug)] + pub struct Builder { + angle: Radians, + stops: [ColorStop; 8], + error: Option, + } -impl Location { - fn to_absolute(self, top_left: Point, size: Size) -> Point { - match self { - Location::TopLeft => top_left, - Location::Top => { - Point::new(top_left.x + size.width / 2.0, top_left.y) - } - Location::TopRight => { - Point::new(top_left.x + size.width, top_left.y) - } - Location::Right => Point::new( - top_left.x + size.width, - top_left.y + size.height / 2.0, - ), - Location::BottomRight => { - Point::new(top_left.x + size.width, top_left.y + size.height) + impl Builder { + /// Creates a new [`Builder`]. + pub fn new(angle: Radians) -> Self { + Self { + angle, + stops: std::array::from_fn(|_| ColorStop { + offset: 2.0, //default offset = invalid + color: Default::default(), + }), + error: None, } - Location::Bottom => Point::new( - top_left.x + size.width / 2.0, - top_left.y + size.height, - ), - Location::BottomLeft => { - Point::new(top_left.x, top_left.y + size.height) + } + + /// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0` or the gradient cannot be built. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if offset.is_finite() && (0.0..=1.0).contains(&offset) { + match self.stops.binary_search_by(|stop| { + stop.offset.partial_cmp(&offset).unwrap() + }) { + Ok(_) => { + self.error = Some(BuilderError::DuplicateOffset(offset)) + } + Err(index) => { + if index < 8 { + self.stops[index] = ColorStop { offset, color }; + } + } + } + } else { + self.error = Some(BuilderError::InvalidOffset(offset)) + }; + + self + } + + /// Adds multiple [`ColorStop`]s to the gradient. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stops( + mut self, + stops: impl IntoIterator, + ) -> Self { + for stop in stops.into_iter() { + self = self.add_stop(stop.offset, stop.color) } - Location::Left => { - Point::new(top_left.x, top_left.y + size.height / 2.0) + + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `BuilderError` if gradient in invalid. + pub fn build(self) -> Result { + if self.stops.is_empty() { + Err(BuilderError::MissingColorStop) + } else if let Some(error) = self.error { + Err(error) + } else { + Ok(Gradient::Linear(Linear { + angle: self.angle, + color_stops: self.stops, + })) } } } + + /// An error that happened when building a [`Linear`] gradient. + #[derive(Debug, thiserror::Error)] + pub enum BuilderError { + #[error("Gradients must contain at least one color stop.")] + /// Gradients must contain at least one color stop. + MissingColorStop, + #[error("Offset {0} must be a unique, finite number.")] + /// Offsets in a gradient must all be unique & finite. + DuplicateOffset(f32), + #[error("Offset {0} must be between 0.0..=1.0.")] + /// Offsets in a gradient must be between 0.0..=1.0. + InvalidOffset(f32), + } } diff --git a/core/src/gradient/linear.rs b/core/src/gradient/linear.rs index c886db479d..e69de29bb2 100644 --- a/core/src/gradient/linear.rs +++ b/core/src/gradient/linear.rs @@ -1,112 +0,0 @@ -//! Linear gradient builder & definition. -use crate::gradient::{ColorStop, Gradient, Position}; -use crate::{Color, Point}; - -/// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. -/// -/// [`Fill`]: crate::widget::canvas::Fill -/// [`Stroke`]: crate::widget::canvas::Stroke -#[derive(Debug, Clone, PartialEq)] -pub struct Linear { - /// The point where the linear gradient begins. - pub start: Point, - /// The point where the linear gradient ends. - pub end: Point, - /// [`ColorStop`]s along the linear gradient path. - pub color_stops: Vec, -} - -/// A [`Linear`] builder. -#[derive(Debug)] -pub struct Builder { - start: Point, - end: Point, - stops: Vec, - error: Option, -} - -impl Builder { - /// Creates a new [`Builder`]. - pub fn new(position: Position) -> Self { - let (start, end) = match position { - Position::Absolute { start, end } => (start, end), - Position::Relative { - top_left, - size, - start, - end, - } => ( - start.to_absolute(top_left, size), - end.to_absolute(top_left, size), - ), - }; - - Self { - start, - end, - stops: vec![], - error: None, - } - } - - /// Adds a new stop, defined by an offset and a color, to the gradient. - /// - /// `offset` must be between `0.0` and `1.0` or the gradient cannot be built. - /// - /// Note: when using the [`glow`] backend, any color stop added after the 16th - /// will not be displayed. - /// - /// On the [`wgpu`] backend this limitation does not exist (technical limit is 524,288 stops). - /// - /// [`glow`]: https://docs.rs/iced_glow - /// [`wgpu`]: https://docs.rs/iced_wgpu - pub fn add_stop(mut self, offset: f32, color: Color) -> Self { - if offset.is_finite() && (0.0..=1.0).contains(&offset) { - match self.stops.binary_search_by(|stop| { - stop.offset.partial_cmp(&offset).unwrap() - }) { - Ok(_) => { - self.error = Some(BuilderError::DuplicateOffset(offset)) - } - Err(index) => { - self.stops.insert(index, ColorStop { offset, color }); - } - } - } else { - self.error = Some(BuilderError::InvalidOffset(offset)) - }; - - self - } - - /// Builds the linear [`Gradient`] of this [`Builder`]. - /// - /// Returns `BuilderError` if gradient in invalid. - pub fn build(self) -> Result { - if self.stops.is_empty() { - Err(BuilderError::MissingColorStop) - } else if let Some(error) = self.error { - Err(error) - } else { - Ok(Gradient::Linear(Linear { - start: self.start, - end: self.end, - color_stops: self.stops, - })) - } - } -} - -/// An error that happened when building a [`Linear`] gradient. -#[derive(Debug, thiserror::Error)] -pub enum BuilderError { - #[error("Gradients must contain at least one color stop.")] - /// Gradients must contain at least one color stop. - MissingColorStop, - #[error("Offset {0} must be a unique, finite number.")] - /// Offsets in a gradient must all be unique & finite. - DuplicateOffset(f32), - #[error("Offset {0} must be between 0.0..=1.0.")] - /// Offsets in a gradient must be between 0.0..=1.0. - InvalidOffset(f32), -} diff --git a/core/src/lib.rs b/core/src/lib.rs index 5bdcee6a4a..198921e3bf 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -42,6 +42,7 @@ pub mod touch; pub mod widget; pub mod window; +mod angle; mod background; mod color; mod content_fit; @@ -57,6 +58,7 @@ mod size; mod vector; pub use alignment::Alignment; +pub use angle::{Degrees, Radians}; pub use background::Background; pub use clipboard::Clipboard; pub use color::Color; diff --git a/core/src/renderer.rs b/core/src/renderer.rs index d6247e39d2..007a0370d2 100644 --- a/core/src/renderer.rs +++ b/core/src/renderer.rs @@ -60,7 +60,7 @@ pub struct Quad { pub border_color: Color, } -/// The border radi for the corners of a graphics primitive in the order: +/// The border radii for the corners of a graphics primitive in the order: /// top-left, top-right, bottom-right, bottom-left. #[derive(Debug, Clone, Copy, PartialEq, Default)] pub struct BorderRadius([f32; 4]); diff --git a/examples/modern_art/Cargo.toml b/examples/modern_art/Cargo.toml deleted file mode 100644 index 4242d209d9..0000000000 --- a/examples/modern_art/Cargo.toml +++ /dev/null @@ -1,11 +0,0 @@ -[package] -name = "modern_art" -version = "0.1.0" -authors = ["Bingus "] -edition = "2021" -publish = false - -[dependencies] -iced = { path = "../..", features = ["canvas", "tokio", "debug"] } -rand = "0.8.5" -env_logger = "0.9" diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs index a43a2b2b0e..e69de29bb2 100644 --- a/examples/modern_art/src/main.rs +++ b/examples/modern_art/src/main.rs @@ -1,143 +0,0 @@ -use iced::widget::canvas::{ - self, gradient::Location, gradient::Position, Cache, Canvas, Cursor, Frame, - Geometry, Gradient, -}; -use iced::{ - executor, Application, Color, Command, Element, Length, Point, Rectangle, - Renderer, Settings, Size, Theme, -}; -use rand::{thread_rng, Rng}; - -fn main() -> iced::Result { - env_logger::builder().format_timestamp(None).init(); - - ModernArt::run(Settings { - antialiasing: true, - ..Settings::default() - }) -} - -#[derive(Debug, Clone, Copy)] -enum Message {} - -struct ModernArt { - cache: Cache, -} - -impl Application for ModernArt { - type Executor = executor::Default; - type Message = Message; - type Theme = Theme; - type Flags = (); - - fn new(_flags: Self::Flags) -> (Self, Command) { - ( - ModernArt { - cache: Default::default(), - }, - Command::none(), - ) - } - - fn title(&self) -> String { - String::from("Modern Art") - } - - fn update(&mut self, _message: Message) -> Command { - Command::none() - } - - fn view(&self) -> Element<'_, Self::Message, Renderer> { - Canvas::new(self) - .width(Length::Fill) - .height(Length::Fill) - .into() - } -} - -impl canvas::Program for ModernArt { - type State = (); - - fn draw( - &self, - _state: &Self::State, - renderer: &Renderer, - _theme: &Theme, - bounds: Rectangle, - _cursor: Cursor, - ) -> Vec { - let geometry = self.cache.draw(renderer, bounds.size(), |frame| { - let num_squares = thread_rng().gen_range(0..1200); - - let mut i = 0; - while i <= num_squares { - generate_box(frame, bounds.size()); - i += 1; - } - }); - - vec![geometry] - } -} - -fn random_direction() -> Location { - match thread_rng().gen_range(0..8) { - 0 => Location::TopLeft, - 1 => Location::Top, - 2 => Location::TopRight, - 3 => Location::Right, - 4 => Location::BottomRight, - 5 => Location::Bottom, - 6 => Location::BottomLeft, - 7 => Location::Left, - _ => Location::TopLeft, - } -} - -fn generate_box(frame: &mut Frame, bounds: Size) -> bool { - let solid = rand::random::(); - - let random_color = || -> Color { - Color::from_rgb( - thread_rng().gen_range(0.0..1.0), - thread_rng().gen_range(0.0..1.0), - thread_rng().gen_range(0.0..1.0), - ) - }; - - let gradient = |top_left: Point, size: Size| -> Gradient { - let mut builder = Gradient::linear(Position::Relative { - top_left, - size, - start: random_direction(), - end: random_direction(), - }); - let stops = thread_rng().gen_range(1..15u32); - - let mut i = 0; - while i <= stops { - builder = builder.add_stop(i as f32 / stops as f32, random_color()); - i += 1; - } - - builder.build().unwrap() - }; - - let top_left = Point::new( - thread_rng().gen_range(0.0..bounds.width), - thread_rng().gen_range(0.0..bounds.height), - ); - - let size = Size::new( - thread_rng().gen_range(50.0..200.0), - thread_rng().gen_range(50.0..200.0), - ); - - if solid { - frame.fill_rectangle(top_left, size, random_color()); - } else { - frame.fill_rectangle(top_left, size, gradient(top_left, size)); - }; - - solid -} diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index f2606feb82..5494ce4bfd 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -10,9 +10,8 @@ use iced::application; use iced::executor; use iced::theme::{self, Theme}; use iced::widget::canvas; -use iced::widget::canvas::gradient::{self, Gradient}; use iced::widget::canvas::stroke::{self, Stroke}; -use iced::widget::canvas::{Cursor, Path}; +use iced::widget::canvas::{Cursor, Gradient, Path}; use iced::window; use iced::{ Application, Color, Command, Element, Length, Point, Rectangle, Renderer, @@ -208,15 +207,14 @@ impl canvas::Program for State { let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS); - let earth_fill = - Gradient::linear(gradient::Position::Absolute { - start: Point::new(-Self::EARTH_RADIUS, 0.0), - end: Point::new(Self::EARTH_RADIUS, 0.0), - }) - .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) - .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)) - .build() - .expect("Build Earth fill gradient"); + let earth_fill = Gradient::linear( + Point::new(-Self::EARTH_RADIUS, 0.0), + Point::new(Self::EARTH_RADIUS, 0.0), + ) + .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) + .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)) + .build() + .expect("Build Earth fill gradient"); frame.fill(&earth, earth_fill); diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index de063d0086..878667f43e 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,10 +1,11 @@ -use iced::alignment; use iced::theme; +use iced::theme::Palette; use iced::widget::{ checkbox, column, container, horizontal_space, image, radio, row, scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; +use iced::{alignment, widget, Degrees, Gradient, Radians, Theme}; use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; pub fn main() -> iced::Result { @@ -53,9 +54,9 @@ impl Sandbox for Tour { if steps.has_previous() { controls = controls.push( - button("Back") - .on_press(Message::BackPressed) - .style(theme::Button::Secondary), + button("Back").on_press(Message::BackPressed).style( + theme::Button::Custom(Box::new(CustomButtonStyle::Danger)), + ), ); } @@ -63,9 +64,9 @@ impl Sandbox for Tour { if steps.can_continue() { controls = controls.push( - button("Next") - .on_press(Message::NextPressed) - .style(theme::Button::Primary), + button("Next").on_press(Message::NextPressed).style( + theme::Button::Custom(Box::new(CustomButtonStyle::Success)), + ), ); } @@ -670,3 +671,39 @@ pub enum Layout { Row, Column, } + +enum CustomButtonStyle { + Success, + Danger, +} + +impl widget::button::StyleSheet for CustomButtonStyle { + type Style = Theme; + + fn active(&self, _style: &Self::Style) -> widget::button::Appearance { + match self { + CustomButtonStyle::Success => widget::button::Appearance { + background: Gradient::linear(Degrees(270.0)) + .add_stop(0.0, Palette::LIGHT.primary) + .add_stop(1.0, Color::from_rgb8(54, 80, 168)) + .build() + .expect("Build gradient") + .into(), + text_color: Color::WHITE, + ..Default::default() + }, + CustomButtonStyle::Danger => widget::button::Appearance { + background: Gradient::linear(Radians( + 3.0 * std::f32::consts::PI / 2.0, + )) + .add_stop(0.0, Palette::LIGHT.danger) + .add_stop(1.0, Color::from_rgb8(125, 26, 24)) + .build() + .expect("Build gradient") + .into(), + text_color: Color::WHITE, + ..Default::default() + }, + } + } +} diff --git a/glow/src/program.rs b/glow/src/program.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/glow/src/shader/includes/border_radius.vert b/glow/src/shader/includes/border_radius.vert new file mode 100644 index 0000000000..891c65e220 --- /dev/null +++ b/glow/src/shader/includes/border_radius.vert @@ -0,0 +1,9 @@ +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} diff --git a/glow/src/shader/includes/gradient.frag b/glow/src/shader/includes/gradient.frag new file mode 100644 index 0000000000..ff16ecbff6 --- /dev/null +++ b/glow/src/shader/includes/gradient.frag @@ -0,0 +1,61 @@ +vec4 gradient( + vec4 direction, + vec2 raw_position, + vec4 offsets[2], + vec4 colors[8] +) { + vec2 start = direction.xy; + vec2 end = direction.zw; + + vec2 v1 = vec2(end - start); + vec2 v2 = vec2(raw_position - start); + vec2 unit = normalize(v1); + float coord_offset = dot(unit, v2) / length(v1); + + //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill + vec4 color; + + float offsets_arr[8]; + offsets_arr[0] = offsets[0].x; + offsets_arr[1] = offsets[0].y; + offsets_arr[2] = offsets[0].z; + offsets_arr[3] = offsets[0].w; + offsets_arr[4] = offsets[1].x; + offsets_arr[5] = offsets[1].y; + offsets_arr[6] = offsets[1].z; + offsets_arr[7] = offsets[1].w; + + int last_index = 7; + for (int i = 0; i <= 7; i++) { + if (offsets_arr[i] > 1.0) { + last_index = i - 1; + break; + } + } + + for (int i = 0; i < last_index; i++) { + float curr_offset = offsets_arr[i]; + float next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + //current coordinate is before the first defined offset, set it to the start color + color = colors[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + //current fragment is between the current offset & the next one, interpolate colors + color = mix(colors[i], colors[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + //current coordinate is after the last defined offset, set it to the last color + color = colors[last_index]; + } + } + + return color; +} diff --git a/glow/src/shader/includes/quad_distance.vert b/glow/src/shader/includes/quad_distance.vert new file mode 100644 index 0000000000..54e648148f --- /dev/null +++ b/glow/src/shader/includes/quad_distance.vert @@ -0,0 +1,17 @@ +float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) +{ + // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN + vec2 inner_size = size - vec2(radius, radius) * 2.0; + vec2 top_left = position + vec2(radius, radius); + vec2 bottom_right = top_left + inner_size; + + vec2 top_left_distance = top_left - frag_coord; + vec2 bottom_right_distance = frag_coord - bottom_right; + + vec2 distance = vec2( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(distance.x * distance.x + distance.y * distance.y); +} diff --git a/glow/src/shader/quad/compatibility/gradient.frag b/glow/src/shader/quad/compatibility/gradient.frag new file mode 100644 index 0000000000..1f79b1cd75 --- /dev/null +++ b/glow/src/shader/quad/compatibility/gradient.frag @@ -0,0 +1,64 @@ +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#endif + +// #includes + +uniform float u_screen_height; + +varying vec4 v_colors[8]; +varying vec4 v_offsets[2]; +varying vec4 v_direction; +varying vec4 v_position_and_size; +varying vec4 v_border_color; +varying vec4 v_border_radius; +varying float v_border_width; + +void main() { + vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); + + vec2 v_position = v_position_and_size.xy; + vec2 v_size = v_position_and_size.zw; + + float border_radius = selectBorderRadius( + v_border_radius, + fragCoord, + (v_position + v_size * 0.5).xy + ); + + float internal_border = max(border_radius - v_border_width, 0.0); + + float internal_distance = fDistance( + fragCoord, + v_position + vec2(v_border_width), + v_size - vec2(v_border_width * 2.0), + internal_border + ); + + float border_mix = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + vec4 mixed_color = mix( + gradient(v_direction, fragCoord, v_offsets, v_colors), + v_border_color, + border_mix + ); + + float d = fDistance( + fragCoord, + v_position, + v_size, + border_radius + ); + + float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); + + gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); +} diff --git a/glow/src/shader/quad/compatibility/gradient.vert b/glow/src/shader/quad/compatibility/gradient.vert new file mode 100644 index 0000000000..49696be2a7 --- /dev/null +++ b/glow/src/shader/quad/compatibility/gradient.vert @@ -0,0 +1,72 @@ +uniform mat4 u_transform; +uniform float u_scale; + +//gradient +attribute vec4 i_colors_1; +attribute vec4 i_colors_2; +attribute vec4 i_colors_3; +attribute vec4 i_colors_4; +attribute vec4 i_colors_5; +attribute vec4 i_colors_6; +attribute vec4 i_colors_7; +attribute vec4 i_colors_8; +attribute vec4 i_offsets_1; +attribute vec4 i_offsets_2; +attribute vec4 i_direction; +//quad properties +attribute vec4 i_position_and_size; +attribute vec4 i_border_color; +attribute vec4 i_border_radius; +attribute float i_border_width; +attribute vec2 i_quad_position; + +varying vec4 v_colors[8]; +varying vec4 v_offsets[2]; +varying vec4 v_direction; +varying vec4 v_position_and_size; +varying vec4 v_border_color; +varying vec4 v_border_radius; +varying float v_border_width; + +void main() { + vec2 i_position = i_position_and_size.xy; + vec2 i_size = i_position_and_size.zw; + + vec2 p_position = i_position * u_scale; + vec2 p_size = i_size * u_scale; + + vec4 i_border_radius = vec4( + min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) + ); + + mat4 i_transform = mat4( + vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), + vec4(0.0, p_size.y + 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) + ); + + // array initializers are not supported in GLSL 1.0 (ES 2.0) + v_colors[0] = i_colors_1; + v_colors[1] = i_colors_2; + v_colors[2] = i_colors_3; + v_colors[3] = i_colors_4; + v_colors[4] = i_colors_5; + v_colors[5] = i_colors_6; + v_colors[6] = i_colors_7; + v_colors[7] = i_colors_8; + + v_offsets[0] = i_offsets_1; + v_offsets[1] = i_offsets_2; + + v_direction = i_direction * u_scale; + v_position_and_size = vec4(p_position, p_size); + v_border_color = i_border_color; + v_border_radius = i_border_radius * u_scale; + v_border_width = i_border_width * u_scale; + + gl_Position = u_transform * i_transform * vec4(i_quad_position, 0.0, 1.0); +} diff --git a/glow/src/shader/quad/compatibility/solid.frag b/glow/src/shader/quad/compatibility/solid.frag new file mode 100644 index 0000000000..202ec9c620 --- /dev/null +++ b/glow/src/shader/quad/compatibility/solid.frag @@ -0,0 +1,56 @@ +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#endif + +// #includes + +uniform float u_screen_height; + +varying vec4 v_color; +varying vec4 v_border_color; +varying vec2 v_position; +varying vec2 v_size; +varying vec4 v_border_radius; +varying float v_border_width; + +void main() { + vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); + + float border_radius = selectBorderRadius( + v_border_radius, + fragCoord, + (v_position + v_size * 0.5).xy + ); + + float internal_border = max(border_radius - v_border_width, 0.0); + + float internal_distance = fDistance( + fragCoord, + v_position + vec2(v_border_width), + v_size - vec2(v_border_width * 2.0), + internal_border + ); + + float border_mix = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + vec4 mixed_color = mix(v_color, v_border_color, border_mix); + + float d = fDistance( + fragCoord, + v_position, + v_size, + border_radius + ); + + float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); + + gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); +} diff --git a/glow/src/shader/quad/compatibility/solid.vert b/glow/src/shader/quad/compatibility/solid.vert new file mode 100644 index 0000000000..bbac512eb6 --- /dev/null +++ b/glow/src/shader/quad/compatibility/solid.vert @@ -0,0 +1,45 @@ +uniform mat4 u_transform; +uniform float u_scale; + +attribute vec4 i_color; +attribute vec2 i_position; +attribute vec2 i_size; +attribute vec4 i_border_color; +attribute vec4 i_border_radius; +attribute float i_border_width; +attribute vec2 i_quad_position; + +varying vec4 v_color; +varying vec2 v_position; +varying vec2 v_size; +varying vec4 v_border_color; +varying vec4 v_border_radius; +varying float v_border_width; + +void main() { + vec2 p_position = i_position * u_scale; + vec2 p_size = i_size * u_scale; + + vec4 i_border_radius = vec4( + min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) + ); + + mat4 i_transform = mat4( + vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), + vec4(0.0, p_size.y + 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) + ); + + v_color = i_color; + v_border_color = i_border_color; + v_position = p_position; + v_size = p_size; + v_border_radius = i_border_radius * u_scale; + v_border_width = i_border_width * u_scale; + + gl_Position = u_transform * i_transform * vec4(i_quad_position, 0.0, 1.0); +} diff --git a/glow/src/shader/quad/core/gradient.frag b/glow/src/shader/quad/core/gradient.frag new file mode 100644 index 0000000000..26d85ee0b8 --- /dev/null +++ b/glow/src/shader/quad/core/gradient.frag @@ -0,0 +1,69 @@ +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#endif + +#ifdef HIGHER_THAN_300 +out vec4 fragColor; +#define gl_FragColor fragColor +#endif + +// #includes + +uniform float u_screen_height; + +in vec4 v_colors[8]; +in vec4 v_offsets[2]; +in vec4 v_direction; +in vec4 v_position_and_size; +in vec4 v_border_color; +in vec4 v_border_radius; +in float v_border_width; + +void main() { + vec2 v_position = v_position_and_size.xy; + vec2 v_size = v_position_and_size.zw; + + vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); + + vec4 mixed_color = gradient(v_direction, fragCoord, v_offsets, v_colors); + + float border_radius = selectBorderRadius( + v_border_radius, + fragCoord, + (v_position + v_size * 0.5).xy + ); + + if (v_border_width > 0.0) { + float internal_border = max(border_radius - v_border_width, 0.0); + + float internal_distance = fDistance( + fragCoord, + v_position + vec2(v_border_width), + v_size - vec2(v_border_width * 2.0), + internal_border + ); + + float border_mix = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + mixed_color = mix(mixed_color, v_border_color, border_mix); + } + + float d = fDistance( + fragCoord, + v_position, + v_size, + border_radius + ); + + float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); + + gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); +} diff --git a/glow/src/shader/quad/core/gradient.vert b/glow/src/shader/quad/core/gradient.vert new file mode 100644 index 0000000000..c003889370 --- /dev/null +++ b/glow/src/shader/quad/core/gradient.vert @@ -0,0 +1,80 @@ +uniform mat4 u_transform; +uniform float u_scale; + +in vec4 i_colors_1; +in vec4 i_colors_2; +in vec4 i_colors_3; +in vec4 i_colors_4; +in vec4 i_colors_5; +in vec4 i_colors_6; +in vec4 i_colors_7; +in vec4 i_colors_8; +in vec4 i_offsets_1; +in vec4 i_offsets_2; +in vec4 i_direction; +in vec4 i_position_and_size; +in vec4 i_border_color; +in vec4 i_border_radius; +in float i_border_width; + +out vec4 v_colors[8]; +out vec4 v_offsets[2]; +out vec4 v_direction; +out vec4 v_position_and_size; +out vec4 v_border_color; +out vec4 v_border_radius; +out float v_border_width; + +vec2 positions[4] = vec2[]( + vec2(0.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(1.0, 1.0) +); + +void main() { + vec2 i_position = i_position_and_size.xy; + vec2 i_size = i_position_and_size.zw; + + vec2 q_position = positions[gl_VertexID]; + vec2 p_position = i_position * u_scale; + vec2 p_size = i_size * u_scale; + + vec4 i_border_radius = vec4( + min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) + ); + + mat4 i_transform = mat4( + vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), + vec4(0.0, p_size.y + 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) + ); + + v_colors = vec4[]( + i_colors_1, + i_colors_2, + i_colors_3, + i_colors_4, + i_colors_5, + i_colors_6, + i_colors_7, + i_colors_8 + ); + + v_offsets = vec4[]( + i_offsets_1, + i_offsets_2 + ); + + v_direction = i_direction * u_scale; + v_position_and_size = vec4(p_position, p_size); + v_border_color = i_border_color; + v_border_radius = i_border_radius * u_scale; + v_border_width = i_border_width * u_scale; + + gl_Position = u_transform * i_transform * vec4(q_position, 0.0, 1.0); +} diff --git a/glow/src/shader/quad/core/solid.frag b/glow/src/shader/quad/core/solid.frag new file mode 100644 index 0000000000..1377e42bd7 --- /dev/null +++ b/glow/src/shader/quad/core/solid.frag @@ -0,0 +1,67 @@ +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#endif + +#ifdef HIGHER_THAN_300 +out vec4 fragColor; +#define gl_FragColor fragColor +#endif + +// #includes + +uniform float u_screen_height; + +in vec4 v_color; +in vec4 v_border_color; +in vec2 v_position; +in vec2 v_scale; +in vec4 v_border_radius; +in float v_border_width; + +void main() { + vec4 mixed_color; + + vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); + + float border_radius = selectBorderRadius( + v_border_radius, + fragCoord, + (v_position + v_scale * 0.5).xy + ); + + if (v_border_width > 0.0) { + float internal_border = max(border_radius - v_border_width, 0.0); + + float internal_distance = fDistance( + fragCoord, + v_position + vec2(v_border_width), + v_scale - vec2(v_border_width * 2.0), + internal_border + ); + + float border_mix = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + mixed_color = mix(v_color, v_border_color, border_mix); + } else { + mixed_color = v_color; + } + + float d = fDistance( + fragCoord, + v_position, + v_scale, + border_radius + ); + + float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); + + gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); +} diff --git a/glow/src/shader/quad/core/solid.vert b/glow/src/shader/quad/core/solid.vert new file mode 100644 index 0000000000..2e5f1dd207 --- /dev/null +++ b/glow/src/shader/quad/core/solid.vert @@ -0,0 +1,52 @@ +uniform mat4 u_transform; +uniform float u_scale; + +in vec4 i_color; +in vec2 i_position; +in vec2 i_size; +in vec4 i_border_color; +in vec4 i_border_radius; +in float i_border_width; + +out vec4 v_color; +out vec4 v_border_color; +out vec2 v_position; +out vec2 v_scale; +out vec4 v_border_radius; +out float v_border_width; + +vec2 positions[4] = vec2[]( + vec2(0.0, 0.0), + vec2(0.0, 1.0), + vec2(1.0, 0.0), + vec2(1.0, 1.0) +); + +void main() { + vec2 q_position = positions[gl_VertexID]; + vec2 p_position = i_position * u_scale; + vec2 p_scale = i_size * u_scale; + + vec4 i_BorderRadius = vec4( + min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), + min(i_border_radius.w, min(i_size.x, i_size.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), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) + ); + + v_color = i_color; + v_border_color = i_border_color; + v_position = p_position; + v_scale = p_scale; + v_border_radius = i_border_radius * u_scale; + v_border_width = i_border_width * u_scale; + + gl_Position = u_transform * i_transform * vec4(q_position, 0.0, 1.0); +} diff --git a/glow/src/shader/triangle/gradient.frag b/glow/src/shader/triangle/gradient.frag new file mode 100644 index 0000000000..9e56854098 --- /dev/null +++ b/glow/src/shader/triangle/gradient.frag @@ -0,0 +1,23 @@ +#ifdef GL_ES +#ifdef GL_FRAGMENT_PRECISION_HIGH +precision highp float; +#else +precision mediump float; +#endif +#endif + +#ifdef HIGHER_THAN_300 +out vec4 fragColor; +#define gl_FragColor fragColor +#endif + +// #includes + +in vec2 v_raw_position; +in vec4 v_colors[8]; +in vec4 v_offsets[2]; +in vec4 v_direction; + +void main() { + gl_FragColor = gradient(v_direction, v_raw_position, v_offsets, v_colors); +} diff --git a/glow/src/shader/triangle/gradient.vert b/glow/src/shader/triangle/gradient.vert new file mode 100644 index 0000000000..ab8c59f330 --- /dev/null +++ b/glow/src/shader/triangle/gradient.vert @@ -0,0 +1,42 @@ +uniform mat4 u_transform; + +in vec2 i_position; +in vec4 i_colors_1; +in vec4 i_colors_2; +in vec4 i_colors_3; +in vec4 i_colors_4; +in vec4 i_colors_5; +in vec4 i_colors_6; +in vec4 i_colors_7; +in vec4 i_colors_8; +in vec4 i_offsets_1; +in vec4 i_offsets_2; +in vec4 i_direction; + +out vec2 v_raw_position; +out vec4 v_colors[8]; +out vec4 v_offsets[2]; +out vec4 v_direction; + +void main() { + gl_Position = u_transform * vec4(i_position, 0.0, 1.0); + + v_raw_position = i_position; + + v_colors[0] = i_colors_1; + + // array initializers are not supported in GLSL 1.0 (ES 2.0) + v_colors[0] = i_colors_1; + v_colors[1] = i_colors_2; + v_colors[2] = i_colors_3; + v_colors[3] = i_colors_4; + v_colors[4] = i_colors_5; + v_colors[5] = i_colors_6; + v_colors[6] = i_colors_7; + v_colors[7] = i_colors_8; + + v_offsets[0] = i_offsets_1; + v_offsets[1] = i_offsets_2; + + v_direction = i_direction; +} diff --git a/glow/src/shader/triangle/solid.vert b/glow/src/shader/triangle/solid.vert new file mode 100644 index 0000000000..d0e6d780cb --- /dev/null +++ b/glow/src/shader/triangle/solid.vert @@ -0,0 +1,11 @@ +uniform mat4 u_transform; + +in vec2 i_position; +in vec4 i_color; + +out vec4 v_color; + +void main() { + gl_Position = u_transform * vec4(i_position, 0.0, 1.0); + v_color = i_color; +} diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs new file mode 100644 index 0000000000..e69de29bb2 diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 195b62daa5..50f38d6b70 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -33,7 +33,7 @@ pub enum Primitive { bounds: Rectangle, /// The background of the quad background: Background, - /// The border radius of the quad + /// The border radii of the quad border_radius: [f32; 4], /// The border width of the quad border_width: f32, @@ -75,15 +75,12 @@ pub enum Primitive { /// It can be used to render many kinds of geometry freely. GradientMesh { /// The vertices and indices of the mesh. - buffers: Mesh2D, + buffers: Mesh2D, /// The size of the drawable region of the mesh. /// /// Any geometry that falls out of this region will be clipped. size: Size, - - /// The [`Gradient`] to apply to the mesh. - gradient: Gradient, }, #[cfg(feature = "tiny-skia")] Fill { diff --git a/style/src/button.rs b/style/src/button.rs index a564a2b7ea..ffe755cb54 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -68,6 +68,9 @@ pub trait StyleSheet { a: color.a * 0.5, ..color }), + Background::Gradient(gradient) => { + Background::Gradient(gradient.adjust_alpha(0.5)) + } }), text_color: Color { a: active.text_color.a * 0.5, diff --git a/style/src/theme.rs b/style/src/theme.rs index b507d096c8..fbc3f38663 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -217,6 +217,9 @@ impl button::StyleSheet for Theme { a: color.a * 0.5, ..color }), + Background::Gradient(gradient) => { + Background::Gradient(gradient.adjust_alpha(0.5)) + } }), text_color: Color { a: active.text_color.a * 0.5, diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs index c210dd4ebb..ecfca1025b 100644 --- a/wgpu/src/buffer.rs +++ b/wgpu/src/buffer.rs @@ -1,7 +1,3 @@ -//! Utilities for buffer operations. -pub mod dynamic; -pub mod r#static; - use std::marker::PhantomData; use std::ops::RangeBounds; @@ -87,3 +83,253 @@ fn next_copy_size(amount: usize) -> u64 { & !align_mask) .max(wgpu::COPY_BUFFER_ALIGNMENT) } + +//TODO(shan) +/// A generic buffer struct useful for items which have no alignment requirements +/// (e.g. Vertex, Index buffers) & no dynamic offsets. +#[derive(Debug)] +pub struct Static { + //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer + offsets: Vec, + label: &'static str, + usages: wgpu::BufferUsages, + gpu: wgpu::Buffer, + size: wgpu::BufferAddress, + _data: PhantomData, +} + +impl Static { + /// Initialize a new static buffer. + pub fn new( + device: &wgpu::Device, + label: &'static str, + usages: wgpu::BufferUsages, + count: usize, + ) -> Self { + let size = (mem::size_of::() * count) as u64; + + Self { + offsets: Vec::new(), + label, + usages, + gpu: Self::gpu_buffer(device, label, size, usages), + size, + _data: PhantomData, + } + } + + 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. + pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool { + let size = (mem::size_of::() * new_count) as u64; + + if self.size < size { + self.offsets.clear(); + self.size = size; + self.gpu = Self::gpu_buffer(device, self.label, size, self.usages); + true + } else { + false + } + } + + /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. + /// + /// Returns the size of the written bytes. + 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) { + let mut buffer = staging_belt.write_buffer( + encoder, + &self.gpu, + offset, + buffer_size, + device, + ); + + buffer.copy_from_slice(bytes); + + self.offsets.push(offset); + } + + bytes_size + } + + fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { + self.offsets + .get(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 + /// 1 that we stored earlier when writing. + pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { + self.gpu.slice(self.offset_at(index)..) + } + + /// Clears any temporary data from the buffer. + pub fn clear(&mut self) { + self.offsets.clear() + } + + /// Returns a reference to the GPU buffer. + pub fn raw(&self) -> &wgpu::Buffer { + &self.gpu + } +} + +/// A dynamic uniform buffer is any type of buffer which does not have a static offset. +pub struct DynamicUniform { + offsets: Vec, + cpu: encase::DynamicUniformBuffer>, + gpu: wgpu::Buffer, + label: &'static str, + size: u64, + _data: PhantomData, +} + +impl Debug for DynamicUniform { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + write!( + f, + "{:?}, {:?}, {:?}, {:?}", + self.offsets, self.gpu, self.label, self.size + ) + } +} + +impl DynamicUniform { + pub fn new(device: &wgpu::Device, label: &'static str) -> Self { + let initial_size = u64::from(T::min_size()); + + Self { + offsets: Vec::new(), + cpu: encase::DynamicUniformBuffer::new(Vec::new()), + gpu: DynamicUniform::::create_gpu_buffer( + device, + label, + wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + initial_size, + ), + label, + size: initial_size, + _data: Default::default(), + } + } + + fn create_gpu_buffer( + device: &wgpu::Device, + label: &'static str, + usage: wgpu::BufferUsages, + size: u64, + ) -> wgpu::Buffer { + device.create_buffer(&wgpu::BufferDescriptor { + label: Some(label), + size, + usage, + mapped_at_creation: false, + }) + } + + /// Write a new value to the CPU buffer with proper alignment. Stores the returned offset value + /// in the buffer for future use. + pub fn push(&mut self, value: &T) { + //this write operation on the cpu buffer will adjust for uniform alignment requirements + let offset = self + .cpu + .write(value) + .expect("Error when writing to dynamic uniform buffer.") + as wgpu::DynamicOffset; + + self.offsets.push(offset); + } + + /// Resize buffer contents if necessary. This will re-create the GPU buffer if current size is + /// less than the newly computed size from the CPU buffer. + /// + /// If the gpu buffer is resized, its bind group will need to be recreated! + pub fn resize(&mut self, device: &wgpu::Device) -> bool { + let new_size = self.cpu.as_ref().len() as u64; + + if self.size < new_size { + self.gpu = DynamicUniform::::create_gpu_buffer( + device, + self.label, + wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + new_size, + ); + self.size = new_size; + true + } else { + false + } + } + + /// Write the contents of this dynamic uniform buffer to the GPU via staging belt command. + pub fn write( + &mut self, + device: &wgpu::Device, + staging_belt: &mut wgpu::util::StagingBelt, + encoder: &mut wgpu::CommandEncoder, + ) { + let size = self.cpu.as_ref().len(); + + if let Some(buffer_size) = wgpu::BufferSize::new(size as u64) { + let mut buffer = staging_belt.write_buffer( + encoder, + &self.gpu, + 0, + buffer_size, + device, + ); + + buffer.copy_from_slice(self.cpu.as_ref()); + } + } + + // Gets the aligned offset at the given index from the CPU buffer. + pub fn offset_at_index(&self, index: usize) -> wgpu::DynamicOffset { + let offset = self + .offsets + .get(index) + .copied() + .expect("Index not found in offsets."); + + offset + } + + /// Returns a reference to the GPU buffer. + pub fn raw(&self) -> &wgpu::Buffer { + &self.gpu + } + + /// Reset the buffer. + pub fn clear(&mut self) { + self.offsets.clear(); + self.cpu.as_mut().clear(); + self.cpu.set_offset(0); + } +} diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs index 43fc47ac71..e69de29bb2 100644 --- a/wgpu/src/buffer/dynamic.rs +++ b/wgpu/src/buffer/dynamic.rs @@ -1,202 +0,0 @@ -//! Utilities for uniform buffer operations. -use encase::private::WriteInto; -use encase::ShaderType; - -use std::fmt; -use std::marker::PhantomData; - -/// A dynamic buffer is any type of buffer which does not have a static offset. -#[derive(Debug)] -pub struct Buffer { - offsets: Vec, - cpu: Internal, - gpu: wgpu::Buffer, - label: &'static str, - size: u64, - _data: PhantomData, -} - -impl Buffer { - /// Creates a new dynamic uniform buffer. - pub fn uniform(device: &wgpu::Device, label: &'static str) -> Self { - Buffer::new( - device, - Internal::Uniform(encase::DynamicUniformBuffer::new(Vec::new())), - label, - wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - ) - } - - #[cfg(not(target_arch = "wasm32"))] - /// Creates a new dynamic storage buffer. - pub fn storage(device: &wgpu::Device, label: &'static str) -> Self { - Buffer::new( - device, - Internal::Storage(encase::DynamicStorageBuffer::new(Vec::new())), - label, - wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, - ) - } - - fn new( - device: &wgpu::Device, - dynamic_buffer_type: Internal, - label: &'static str, - usage: wgpu::BufferUsages, - ) -> Self { - let initial_size = u64::from(T::min_size()); - - Self { - offsets: Vec::new(), - cpu: dynamic_buffer_type, - gpu: Buffer::::create_gpu_buffer( - device, - label, - usage, - initial_size, - ), - label, - size: initial_size, - _data: Default::default(), - } - } - - fn create_gpu_buffer( - device: &wgpu::Device, - label: &'static str, - usage: wgpu::BufferUsages, - size: u64, - ) -> wgpu::Buffer { - device.create_buffer(&wgpu::BufferDescriptor { - label: Some(label), - size, - usage, - mapped_at_creation: false, - }) - } - - /// Write a new value to the CPU buffer with proper alignment. Stores the returned offset value - /// in the buffer for future use. - pub fn push(&mut self, value: &T) { - //this write operation on the cpu buffer will adjust for uniform alignment requirements - let offset = self.cpu.write(value); - self.offsets.push(offset); - } - - /// Resize buffer contents if necessary. This will re-create the GPU buffer if current size is - /// less than the newly computed size from the CPU buffer. - /// - /// If the gpu buffer is resized, its bind group will need to be recreated! - pub fn resize(&mut self, device: &wgpu::Device) -> bool { - let new_size = self.cpu.get_ref().len() as u64; - - if self.size < new_size { - let usages = match self.cpu { - Internal::Uniform(_) => { - wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST - } - #[cfg(not(target_arch = "wasm32"))] - Internal::Storage(_) => { - wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST - } - }; - - self.gpu = Buffer::::create_gpu_buffer( - device, self.label, usages, new_size, - ); - self.size = new_size; - true - } else { - false - } - } - - /// Write the contents of this dynamic buffer to the GPU via staging belt command. - pub fn write(&mut self, queue: &wgpu::Queue) { - queue.write_buffer(&self.gpu, 0, self.cpu.get_ref()); - } - - // Gets the aligned offset at the given index from the CPU buffer. - pub fn offset_at_index(&self, index: usize) -> wgpu::DynamicOffset { - let offset = self - .offsets - .get(index) - .copied() - .expect("Index not found in offsets."); - - offset - } - - /// Returns a reference to the GPU buffer. - pub fn raw(&self) -> &wgpu::Buffer { - &self.gpu - } - - /// Reset the buffer. - pub fn clear(&mut self) { - self.offsets.clear(); - self.cpu.clear(); - } -} - -// Currently supported dynamic buffers. -enum Internal { - Uniform(encase::DynamicUniformBuffer>), - #[cfg(not(target_arch = "wasm32"))] - //storage buffers are not supported on wgpu wasm target (yet) - Storage(encase::DynamicStorageBuffer>), -} - -impl Internal { - /// Writes the current value to its CPU buffer with proper alignment. - pub(super) fn write( - &mut self, - value: &T, - ) -> wgpu::DynamicOffset { - match self { - Internal::Uniform(buf) => buf - .write(value) - .expect("Error when writing to dynamic uniform buffer.") - as u32, - #[cfg(not(target_arch = "wasm32"))] - Internal::Storage(buf) => buf - .write(value) - .expect("Error when writing to dynamic storage buffer.") - as u32, - } - } - - /// Returns bytearray of aligned CPU buffer. - pub(super) fn get_ref(&self) -> &[u8] { - match self { - Internal::Uniform(buf) => buf.as_ref(), - #[cfg(not(target_arch = "wasm32"))] - Internal::Storage(buf) => buf.as_ref(), - } - } - - /// Resets the CPU buffer. - pub(super) fn clear(&mut self) { - match self { - Internal::Uniform(buf) => { - buf.as_mut().clear(); - buf.set_offset(0); - } - #[cfg(not(target_arch = "wasm32"))] - Internal::Storage(buf) => { - buf.as_mut().clear(); - buf.set_offset(0); - } - } - } -} - -impl fmt::Debug for Internal { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Uniform(_) => write!(f, "Internal::Uniform(_)"), - #[cfg(not(target_arch = "wasm32"))] - Self::Storage(_) => write!(f, "Internal::Storage(_)"), - } - } -} diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 59ec31fef6..db5c43b858 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -25,7 +25,7 @@ pub struct Frame { enum Buffer { Solid(tessellation::VertexBuffers), Gradient( - tessellation::VertexBuffers, + tessellation::VertexBuffers, Gradient, ), } @@ -49,12 +49,11 @@ impl BufferStack { )); } }, - Style::Gradient(gradient) => match self.stack.last() { - Some(Buffer::Gradient(_, last)) if gradient == last => {} + Style::Gradient(_) => match self.stack.last() { + Some(Buffer::Gradient(_)) => {} _ => { self.stack.push(Buffer::Gradient( tessellation::VertexBuffers::new(), - gradient.clone(), )); } }, @@ -74,9 +73,14 @@ impl BufferStack { TriangleVertex2DBuilder(color.into_linear()), )) } - (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( - tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), - ), + (Style::Gradient(gradient), Buffer::Gradient(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + GradientVertex2DBuilder { + gradient: gradient.clone(), + }, + )) + } _ => unreachable!(), } } @@ -92,9 +96,14 @@ impl BufferStack { TriangleVertex2DBuilder(color.into_linear()), )) } - (Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new( - tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder), - ), + (Style::Gradient(gradient), Buffer::Gradient(buffer)) => { + Box::new(tessellation::BuffersBuilder::new( + buffer, + GradientVertex2DBuilder { + gradient: gradient.clone(), + }, + )) + } _ => unreachable!(), } } @@ -132,11 +141,13 @@ impl Transform { } fn transform_gradient(&self, mut gradient: Gradient) -> Gradient { - let (start, end) = match &mut gradient { - Gradient::Linear(linear) => (&mut linear.start, &mut linear.end), - }; - self.transform_point(start); - self.transform_point(end); + match &mut gradient { + Gradient::Linear(linear) => { + self.transform_point(&mut linear.start); + self.transform_point(&mut linear.end); + } + } + gradient } } @@ -457,7 +468,7 @@ impl Frame { }) } } - Buffer::Gradient(buffer, gradient) => { + Buffer::Gradient(buffer) => { if !buffer.indices.is_empty() { self.primitives.push(Primitive::GradientMesh { buffers: primitive::Mesh2D { @@ -465,7 +476,6 @@ impl Frame { indices: buffer.indices, }, size: self.size, - gradient, }) } } @@ -476,34 +486,38 @@ impl Frame { } } -struct Vertex2DBuilder; +struct GradientVertex2DBuilder { + gradient: Gradient, +} -impl tessellation::FillVertexConstructor - for Vertex2DBuilder +impl tessellation::FillVertexConstructor + for GradientVertex2DBuilder { fn new_vertex( &mut self, vertex: tessellation::FillVertex<'_>, - ) -> primitive::Vertex2D { + ) -> primitive::GradientVertex2D { let position = vertex.position(); - primitive::Vertex2D { + primitive::GradientVertex2D { position: [position.x, position.y], + gradient: self.gradient.flatten(), } } } -impl tessellation::StrokeVertexConstructor - for Vertex2DBuilder +impl tessellation::StrokeVertexConstructor + for GradientVertex2DBuilder { fn new_vertex( &mut self, vertex: tessellation::StrokeVertex<'_, '_>, - ) -> primitive::Vertex2D { + ) -> primitive::GradientVertex2D { let position = vertex.position(); - primitive::Vertex2D { + triangle::GradientVertex2D { position: [position.x, position.y], + gradient: self.gradient.flatten(), } } } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index cb9d5e2f69..d422aed8aa 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -1,13 +1,12 @@ //! Organize rendering primitives into a flattened list of layers. mod image; -mod quad; mod text; pub mod mesh; +pub mod quad; pub use image::Image; pub use mesh::Mesh; -pub use quad::Quad; pub use text::Text; use crate::core::alignment; @@ -21,7 +20,7 @@ pub struct Layer<'a> { pub bounds: Rectangle, /// The quads of the [`Layer`]. - pub quads: Vec, + pub quads: Quads, /// The triangle meshes of the [`Layer`]. pub meshes: Vec>, @@ -33,12 +32,29 @@ pub struct Layer<'a> { pub images: Vec, } +/// The quads of the [`Layer`]. +#[derive(Default, Debug)] +pub struct Quads { + /// The solid quads of the [`Layer`]. + pub solids: Vec, + + /// The gradient quads of the [`Layer`]. + pub gradients: Vec, +} + +impl Quads { + /// Returns whether both solid & gradient quads are empty or not. + pub fn is_empty(&self) -> bool { + self.solids.is_empty() && self.gradients.is_empty() + } +} + impl<'a> Layer<'a> { /// Creates a new [`Layer`] with the given clipping bounds. pub fn new(bounds: Rectangle) -> Self { Self { bounds, - quads: Vec::new(), + quads: Quads::default(), meshes: Vec::new(), text: Vec::new(), images: Vec::new(), @@ -139,19 +155,36 @@ impl<'a> Layer<'a> { let layer = &mut layers[current_layer]; // TODO: Move some of these computations to the GPU (?) - layer.quads.push(Quad { + let properties = Properties { position: [ bounds.x + translation.x, bounds.y + translation.y, ], size: [bounds.width, bounds.height], - color: match background { - Background::Color(color) => color.into_linear(), - }, + border_color: border_color.into_linear(), border_radius: *border_radius, border_width: *border_width, - border_color: border_color.into_linear(), - }); + }; + + match background { + Background::Color(color) => { + layer.quads.solids.push(quad::Solid { + color: color.into_linear(), + properties, + }); + } + Background::Gradient(gradient) => { + let quad = quad::Gradient { + gradient: (*gradient).flatten(Rectangle::new( + properties.position.into(), + properties.size.into(), + )), + properties, + }; + + layer.quads.gradients.push(quad); + } + }; } Primitive::Image { handle, bounds } => { let layer = &mut layers[current_layer]; @@ -191,11 +224,7 @@ impl<'a> Layer<'a> { }); } } - Primitive::GradientMesh { - buffers, - size, - gradient, - } => { + Primitive::GradientMesh { buffers, size } => { let layer = &mut layers[current_layer]; let bounds = Rectangle::new( @@ -209,7 +238,6 @@ impl<'a> Layer<'a> { origin: Point::new(translation.x, translation.y), buffers, clip_bounds, - gradient, }); } } diff --git a/wgpu/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 9dd143919b..20e44451fa 100644 --- a/wgpu/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -22,13 +22,10 @@ pub enum Mesh<'a> { origin: Point, /// The vertex and index buffers of the [`Mesh`]. - buffers: &'a primitive::Mesh2D, + buffers: &'a primitive::Mesh2D, /// The clipping bounds of the [`Mesh`]. clip_bounds: Rectangle, - - /// The gradient to apply to the [`Mesh`]. - gradient: &'a Gradient, }, } diff --git a/wgpu/src/layer/quad.rs b/wgpu/src/layer/quad.rs index 0d8bde9dde..b5f88e6159 100644 --- a/wgpu/src/layer/quad.rs +++ b/wgpu/src/layer/quad.rs @@ -1,30 +1,51 @@ -/// A colored rectangle with a border. -/// -/// This type can be directly uploaded to GPU memory. -#[derive(Debug, Clone, Copy)] +//! A rectangle with certain styled properties. + +use bytemuck::{Pod, Zeroable}; + +/// The properties of a quad. +#[derive(Clone, Copy, Debug, Pod, Zeroable)] #[repr(C)] -pub struct Quad { - /// The position of the [`Quad`]. +pub struct Properties { + /// The position of the quad. pub position: [f32; 2], - /// The size of the [`Quad`]. + /// The size of the quad. pub size: [f32; 2], - /// The color of the [`Quad`], in __linear RGB__. - pub color: [f32; 4], - - /// The border color of the [`Quad`], in __linear RGB__. + /// The border color of the quad, in __linear RGB__. pub border_color: [f32; 4], - /// The border radius of the [`Quad`]. + /// The border radii of the quad. pub border_radius: [f32; 4], - /// The border width of the [`Quad`]. + /// The border width of the quad. pub border_width: f32, } +/// A quad filled with a solid color. +#[derive(Clone, Copy, Debug, Pod, Zeroable)] +#[repr(C)] +pub struct Solid { + /// The background color data of the quad. + pub color: [f32; 4], + + /// The [`Properties`] of the quad. + pub properties: Properties, +} + +/// A quad filled with interpolated colors. +#[derive(Clone, Copy, Debug)] +#[repr(C)] +pub struct Gradient { + /// The background gradient data of the quad. + pub gradient: [f32; 44], + + /// The [`Properties`] of the quad. + pub properties: Properties, +} + #[allow(unsafe_code)] -unsafe impl bytemuck::Zeroable for Quad {} +unsafe impl Pod for Gradient {} #[allow(unsafe_code)] -unsafe impl bytemuck::Pod for Quad {} +unsafe impl Zeroable for Gradient {} diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index b55216d739..2be21d2d77 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -3,8 +3,6 @@ use crate::graphics::Transformation; use crate::layer; use crate::Buffer; -use bytemuck::{Pod, Zeroable}; -use std::mem; use wgpu::util::DeviceExt; #[cfg(feature = "tracing")] diff --git a/wgpu/src/shader/gradient.wgsl b/wgpu/src/shader/gradient.wgsl deleted file mode 100644 index 63825aec0b..0000000000 --- a/wgpu/src/shader/gradient.wgsl +++ /dev/null @@ -1,88 +0,0 @@ -struct Uniforms { - transform: mat4x4, - //xy = start, wz = end - position: vec4, - //x = start stop, y = end stop, zw = padding - stop_range: vec4, -} - -struct Stop { - color: vec4, - offset: f32, -}; - -@group(0) @binding(0) -var uniforms: Uniforms; - -@group(0) @binding(1) -var color_stops: array; - -struct VertexOutput { - @builtin(position) position: vec4, - @location(0) raw_position: vec2 -} - -@vertex -fn vs_main(@location(0) input: vec2) -> VertexOutput { - var output: VertexOutput; - output.position = uniforms.transform * vec4(input.xy, 0.0, 1.0); - output.raw_position = input; - - return output; -} - -//TODO: rewrite without branching -@fragment -fn fs_main(input: VertexOutput) -> @location(0) vec4 { - let start = uniforms.position.xy; - let end = uniforms.position.zw; - let start_stop = uniforms.stop_range.x; - let end_stop = uniforms.stop_range.y; - - let v1 = end - start; - let v2 = input.raw_position.xy - start; - let unit = normalize(v1); - let offset = dot(unit, v2) / length(v1); - - let min_stop = color_stops[start_stop]; - let max_stop = color_stops[end_stop]; - - var color: vec4; - - if (offset <= min_stop.offset) { - color = min_stop.color; - } else if (offset >= max_stop.offset) { - color = max_stop.color; - } else { - var min = min_stop; - var max = max_stop; - var min_index = start_stop; - var max_index = end_stop; - - loop { - if (min_index >= max_index - 1) { - break; - } - - let index = min_index + (max_index - min_index) / 2; - - let stop = color_stops[index]; - - if (offset <= stop.offset) { - max = stop; - max_index = index; - } else { - min = stop; - min_index = index; - } - } - - color = mix(min.color, max.color, smoothstep( - min.offset, - max.offset, - offset - )); - } - - return color; -} diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index cf4f7e4d5b..9ef20a66f2 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -5,17 +5,50 @@ struct Globals { @group(0) @binding(0) var globals: Globals; -struct VertexInput { +fn distance_alg( + frag_coord: vec2, + position: vec2, + size: vec2, + radius: f32 +) -> f32 { + var inner_size: vec2 = size - vec2(radius, radius) * 2.0; + var top_left: vec2 = position + vec2(radius, radius); + var bottom_right: vec2 = top_left + inner_size; + + var top_left_distance: vec2 = top_left - frag_coord; + var bottom_right_distance: vec2 = frag_coord - bottom_right; + + var dist: vec2 = vec2( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(dist.x * dist.x + dist.y * dist.y); +} + +// Based on the fragement position and the center of the quad, select one of the 4 radi. +// Order matches CSS border radius attribute: +// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left +fn select_border_radius(radi: vec4, position: vec2, center: vec2) -> f32 { + var rx = radi.x; + var ry = radi.y; + rx = select(radi.x, radi.y, position.x > center.x); + ry = select(radi.w, radi.z, position.x > center.x); + rx = select(rx, ry, position.y > center.y); + return rx; +} + +struct SolidVertexInput { @location(0) v_pos: vec2, - @location(1) pos: vec2, - @location(2) scale: vec2, - @location(3) color: vec4, + @location(1) color: vec4, + @location(2) pos: vec2, + @location(3) scale: vec2, @location(4) border_color: vec4, @location(5) border_radius: vec4, @location(6) border_width: f32, } -struct VertexOutput { +struct SolidVertexOutput { @builtin(position) position: vec4, @location(0) color: vec4, @location(1) border_color: vec4, @@ -26,8 +59,8 @@ struct VertexOutput { } @vertex -fn vs_main(input: VertexInput) -> VertexOutput { - var out: VertexOutput; +fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { + var out: SolidVertexOutput; var pos: vec2 = input.pos * globals.scale; var scale: vec2 = input.scale * globals.scale; @@ -47,54 +80,20 @@ fn vs_main(input: VertexInput) -> VertexOutput { vec4(pos - vec2(0.5, 0.5), 0.0, 1.0) ); + out.position = globals.transform * transform * vec4(input.v_pos, 0.0, 1.0); out.color = input.color; out.border_color = input.border_color; out.pos = pos; out.scale = scale; out.border_radius = border_radius * globals.scale; out.border_width = input.border_width * globals.scale; - out.position = globals.transform * transform * vec4(input.v_pos, 0.0, 1.0); return out; } -fn distance_alg( - frag_coord: vec2, - position: vec2, - size: vec2, - radius: f32 -) -> f32 { - var inner_size: vec2 = size - vec2(radius, radius) * 2.0; - var top_left: vec2 = position + vec2(radius, radius); - var bottom_right: vec2 = top_left + inner_size; - - var top_left_distance: vec2 = top_left - frag_coord; - var bottom_right_distance: vec2 = frag_coord - bottom_right; - - var dist: vec2 = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(dist.x * dist.x + dist.y * dist.y); -} - -// Based on the fragement position and the center of the quad, select one of the 4 radi. -// Order matches CSS border radius attribute: -// radi.x = top-left, radi.y = top-right, radi.z = bottom-right, radi.w = bottom-left -fn select_border_radius(radi: vec4, position: vec2, center: vec2) -> f32 { - var rx = radi.x; - var ry = radi.y; - rx = select(radi.x, radi.y, position.x > center.x); - ry = select(radi.w, radi.z, position.x > center.x); - rx = select(rx, ry, position.y > center.y); - return rx; -} - - @fragment -fn fs_main( - input: VertexOutput +fn solid_fs_main( + input: SolidVertexOutput ) -> @location(0) vec4 { var mixed_color: vec4 = input.color; @@ -138,3 +137,208 @@ fn fs_main( return vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); } + +struct GradientVertexInput { + @location(0) v_pos: vec2, + @location(1) color_1: vec4, + @location(2) color_2: vec4, + @location(3) color_3: vec4, + @location(4) color_4: vec4, + @location(5) color_5: vec4, + @location(6) color_6: vec4, + @location(7) color_7: vec4, + @location(8) color_8: vec4, + @location(9) offsets_1: vec4, + @location(10) offsets_2: vec4, + @location(11) direction: vec4, + @location(12) position_and_scale: vec4, + @location(13) border_color: vec4, + @location(14) border_radius: vec4, + @location(15) border_width: f32 +} + +struct GradientVertexOutput { + @builtin(position) position: vec4, + @location(1) color_1: vec4, + @location(2) color_2: vec4, + @location(3) color_3: vec4, + @location(4) color_4: vec4, + @location(5) color_5: vec4, + @location(6) color_6: vec4, + @location(7) color_7: vec4, + @location(8) color_8: vec4, + @location(9) offsets_1: vec4, + @location(10) offsets_2: vec4, + @location(11) direction: vec4, + @location(12) position_and_scale: vec4, + @location(13) border_color: vec4, + @location(14) border_radius: vec4, + @location(15) border_width: f32 +} + +@vertex +fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { + var out: GradientVertexOutput; + + var pos: vec2 = input.position_and_scale.xy * globals.scale; + var scale: vec2 = input.position_and_scale.zw * globals.scale; + + var min_border_radius = min(input.position_and_scale.z, input.position_and_scale.w) * 0.5; + var border_radius: vec4 = vec4( + min(input.border_radius.x, min_border_radius), + min(input.border_radius.y, min_border_radius), + min(input.border_radius.z, min_border_radius), + min(input.border_radius.w, min_border_radius) + ); + + var transform: mat4x4 = mat4x4( + vec4(scale.x + 1.0, 0.0, 0.0, 0.0), + vec4(0.0, scale.y + 1.0, 0.0, 0.0), + vec4(0.0, 0.0, 1.0, 0.0), + vec4(pos - vec2(0.5, 0.5), 0.0, 1.0) + ); + + out.position = globals.transform * transform * vec4(input.v_pos, 0.0, 1.0); + out.color_1 = input.color_1; + out.color_2 = input.color_2; + out.color_3 = input.color_3; + out.color_4 = input.color_4; + out.color_5 = input.color_5; + out.color_6 = input.color_6; + out.color_7 = input.color_7; + out.color_8 = input.color_8; + out.offsets_1 = input.offsets_1; + out.offsets_2 = input.offsets_2; + out.direction = input.direction * globals.scale; + out.position_and_scale = vec4(pos, scale); + out.border_color = input.border_color; + out.border_radius = border_radius * globals.scale; + out.border_width = input.border_width * globals.scale; + + return out; +} + +/// Returns the current interpolated color with a max 8-stop gradient +fn gradient( + raw_position: vec2, + direction: vec4, + colors: array, 8>, + offsets: array, + last_index: i32 +) -> vec4 { + let start = direction.xy; + let end = direction.zw; + + let v1 = end - start; + let v2 = raw_position - start; + let unit = normalize(v1); + let coord_offset = dot(unit, v2) / length(v1); + + //need to store these as a var to use dynamic indexing in a loop + //this is already added to wgsl spec but not in wgpu yet + var colors_arr = colors; + var offsets_arr = offsets; + + var color: vec4; + + for (var i: i32 = 0; i < last_index; i++) { + let curr_offset = offsets_arr[i]; + let next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + color = colors_arr[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + color = mix(colors_arr[i], colors_arr[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset, + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + color = colors_arr[last_index]; + } + } + + return color; +} + +@fragment +fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { + let colors = array, 8>( + input.color_1, + input.color_2, + input.color_3, + input.color_4, + input.color_5, + input.color_6, + input.color_7, + input.color_8, + ); + + var offsets = array( + input.offsets_1.x, + input.offsets_1.y, + input.offsets_1.z, + input.offsets_1.w, + input.offsets_2.x, + input.offsets_2.y, + input.offsets_2.z, + input.offsets_2.w, + ); + + //TODO could just pass this in to the shader but is probably more performant to just check it here + var last_index = 7; + for (var i: i32 = 0; i <= 7; i++) { + if (offsets[i] > 1.0) { + last_index = i - 1; + break; + } + } + + var mixed_color: vec4 = gradient(input.position.xy, input.direction, colors, offsets, last_index); + + let pos = input.position_and_scale.xy; + let scale = input.position_and_scale.zw; + + var border_radius = select_border_radius( + input.border_radius, + input.position.xy, + (pos + scale * 0.5).xy + ); + + if (input.border_width > 0.0) { + var internal_border: f32 = max(border_radius - input.border_width, 0.0); + + var internal_distance: f32 = distance_alg( + input.position.xy, + pos + vec2(input.border_width, input.border_width), + scale - vec2(input.border_width * 2.0, input.border_width * 2.0), + internal_border + ); + + var border_mix: f32 = smoothstep( + max(internal_border - 0.5, 0.0), + internal_border + 0.5, + internal_distance + ); + + mixed_color = mix(mixed_color, input.border_color, vec4(border_mix, border_mix, border_mix, border_mix)); + } + + var dist: f32 = distance_alg( + input.position.xy, + pos, + scale, + border_radius + ); + + var radius_alpha: f32 = 1.0 - smoothstep( + max(border_radius - 0.5, 0.0), + border_radius + 0.5, + dist); + + return vec4(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha); +} diff --git a/wgpu/src/shader/triangle/gradient.wgsl b/wgpu/src/shader/triangle/gradient.wgsl new file mode 100644 index 0000000000..24c330a2e5 --- /dev/null +++ b/wgpu/src/shader/triangle/gradient.wgsl @@ -0,0 +1,139 @@ +struct Globals { + transform: mat4x4, +} + +@group(0) @binding(0) var globals: Globals; + +struct GradientVertexOutput { + @builtin(position) position: vec4, + @location(0) raw_position: vec2, + @location(1) color_1: vec4, + @location(2) color_2: vec4, + @location(3) color_3: vec4, + @location(4) color_4: vec4, + @location(5) color_5: vec4, + @location(6) color_6: vec4, + @location(7) color_7: vec4, + @location(8) color_8: vec4, + @location(9) offsets_1: vec4, + @location(10) offsets_2: vec4, + @location(11) direction: vec4, +} + +@vertex +fn vs_main( + @location(0) input: vec2, + @location(1) color_1: vec4, + @location(2) color_2: vec4, + @location(3) color_3: vec4, + @location(4) color_4: vec4, + @location(5) color_5: vec4, + @location(6) color_6: vec4, + @location(7) color_7: vec4, + @location(8) color_8: vec4, + @location(9) offsets_1: vec4, + @location(10) offsets_2: vec4, + @location(11) direction: vec4, +) -> GradientVertexOutput { + var output: GradientVertexOutput; + + output.position = globals.transform * vec4(input.xy, 0.0, 1.0); + output.raw_position = input; + //pass gradient data to frag shader + output.color_1 = color_1; + output.color_2 = color_2; + output.color_3 = color_3; + output.color_4 = color_4; + output.color_5 = color_5; + output.color_6 = color_6; + output.color_7 = color_7; + output.color_8 = color_8; + output.offsets_1 = offsets_1; + output.offsets_2 = offsets_2; + output.direction = direction; + + return output; +} + +/// Returns the current interpolated color with a max 8-stop gradient +fn gradient( + raw_position: vec2, + direction: vec4, + colors: array, 8>, + offsets: array, + last_index: i32 +) -> vec4 { + let start = direction.xy; + let end = direction.zw; + + let v1 = end - start; + let v2 = raw_position - start; + let unit = normalize(v1); + let coord_offset = dot(unit, v2) / length(v1); + + //need to store these as a var to use dynamic indexing in a loop + //this is already added to wgsl spec but not in wgpu yet + var colors_arr = colors; + var offsets_arr = offsets; + + var color: vec4; + + for (var i: i32 = 0; i < last_index; i++) { + let curr_offset = offsets_arr[i]; + let next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + color = colors_arr[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + color = mix(colors_arr[i], colors_arr[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset, + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + color = colors_arr[last_index]; + } + } + + return color; +} + +@fragment +fn fs_main(input: GradientVertexOutput) -> @location(0) vec4 { + let colors = array, 8>( + input.color_1, + input.color_2, + input.color_3, + input.color_4, + input.color_5, + input.color_6, + input.color_7, + input.color_8, + ); + + var offsets = array( + input.offsets_1.x, + input.offsets_1.y, + input.offsets_1.z, + input.offsets_1.w, + input.offsets_2.x, + input.offsets_2.y, + input.offsets_2.z, + input.offsets_2.w, + ); + + //TODO could just pass this in to the shader but is probably more performant to just check it here + var last_index = 7; + for (var i: i32 = 0; i <= 7; i++) { + if (offsets[i] >= 1.0) { + last_index = i; + break; + } + } + + return gradient(input.raw_position, input.direction, colors, offsets, last_index); +} diff --git a/wgpu/src/shader/solid.wgsl b/wgpu/src/shader/triangle/solid.wgsl similarity index 67% rename from wgpu/src/shader/solid.wgsl rename to wgpu/src/shader/triangle/solid.wgsl index b24402f832..808f7eaa60 100644 --- a/wgpu/src/shader/solid.wgsl +++ b/wgpu/src/shader/triangle/solid.wgsl @@ -4,19 +4,19 @@ struct Globals { @group(0) @binding(0) var globals: Globals; -struct VertexInput { +struct SolidVertexInput { @location(0) position: vec2, @location(1) color: vec4, } -struct VertexOutput { +struct SolidVertexOutput { @builtin(position) position: vec4, @location(0) color: vec4, } @vertex -fn vs_main(input: VertexInput) -> VertexOutput { - var out: VertexOutput; +fn vs_main(input: SolidVertexInput) -> SolidVertexOutput { + var out: SolidVertexOutput; out.color = input.color; out.position = globals.transform * vec4(input.position, 0.0, 1.0); @@ -25,6 +25,6 @@ fn vs_main(input: VertexInput) -> VertexOutput { } @fragment -fn fs_main(input: VertexOutput) -> @location(0) vec4 { +fn fs_main(input: SolidVertexOutput) -> @location(0) vec4 { return input.color; } diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 9fa521d74d..8bee712cb5 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -13,11 +13,7 @@ use tracing::info_span; pub struct Pipeline { blit: Option, solid: solid::Pipeline, - - /// Gradients are currently not supported on WASM targets due to their need of storage buffers. - #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Pipeline, - layers: Vec, prepare_layer: usize, } @@ -27,8 +23,6 @@ struct Layer { index_buffer: Buffer, index_strides: Vec, solid: solid::Layer, - - #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Layer, } @@ -36,7 +30,7 @@ impl Layer { fn new( device: &wgpu::Device, solid: &solid::Pipeline, - #[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline, + gradient: &gradient::Pipeline, ) -> Self { Self { index_buffer: Buffer::new( @@ -46,8 +40,6 @@ impl Layer { ), index_strides: Vec::new(), solid: solid::Layer::new(device, &solid.constants_layout), - - #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Layer::new(device, &gradient.constants_layout), } } @@ -70,31 +62,19 @@ impl Layer { // the majority of use cases. Therefore we will write GPU data every frame (for now). let _ = self.index_buffer.resize(device, count.indices); let _ = self.solid.vertices.resize(device, count.solid_vertices); - - #[cfg(not(target_arch = "wasm32"))] - let _ = self - .gradient - .vertices - .resize(device, count.gradient_vertices); + let _ = self.gradient.vertices.resize(device, count.gradient_vertices); // Prepare dynamic buffers & data store for writing self.index_buffer.clear(); self.index_strides.clear(); self.solid.vertices.clear(); + self.gradient.vertices.clear(); self.solid.uniforms.clear(); - - #[cfg(not(target_arch = "wasm32"))] - { - self.gradient.uniforms.clear(); - self.gradient.vertices.clear(); - self.gradient.storage.clear(); - } + self.gradient.uniforms.clear(); let mut solid_vertex_offset = 0; - let mut index_offset = 0; - - #[cfg(not(target_arch = "wasm32"))] let mut gradient_vertex_offset = 0; + let mut index_offset = 0; for mesh in meshes { let origin = mesh.origin(); @@ -122,10 +102,11 @@ impl Layer { solid_vertex_offset += written_bytes; } - #[cfg(not(target_arch = "wasm32"))] - Mesh::Gradient { - buffers, gradient, .. - } => { + Mesh::Gradient { buffers, .. } => { + self.gradient.uniforms.push(&Uniforms { + transform: transform.into(), + }); + let written_bytes = self.gradient.vertices.write( queue, gradient_vertex_offset, @@ -181,8 +162,6 @@ impl Layer { } } } - #[cfg(target_arch = "wasm32")] - Mesh::Gradient { .. } => {} } } @@ -201,46 +180,32 @@ impl Layer { self.solid.uniforms.write(queue); } - #[cfg(not(target_arch = "wasm32"))] if count.gradient_vertices > 0 { - // First write the pending color stops to the CPU buffer - self.gradient - .storage - .push(&self.gradient.color_stops_pending_write); - // Resize buffers if needed let uniforms_resized = self.gradient.uniforms.resize(device); - let storage_resized = self.gradient.storage.resize(device); - if uniforms_resized || storage_resized { + if uniforms_resized { self.gradient.constants = gradient::Layer::bind_group( device, self.gradient.uniforms.raw(), - self.gradient.storage.raw(), - &gradient.constants_layout, + &self.gradient.bind_group_layout, ); } // Write to GPU self.gradient.uniforms.write(queue); - self.gradient.storage.write(queue); - - // Cleanup - self.gradient.color_stop_offset = 0; - self.gradient.color_stops_pending_write.color_stops.clear(); } } fn render<'a>( &'a self, solid: &'a solid::Pipeline, - #[cfg(not(target_arch = "wasm32"))] gradient: &'a gradient::Pipeline, + gradient: &'a gradient::Pipeline, meshes: &[Mesh<'_>], scale_factor: f32, render_pass: &mut wgpu::RenderPass<'a>, ) { let mut num_solids = 0; - #[cfg(not(target_arch = "wasm32"))] let mut num_gradients = 0; let mut last_is_solid = None; @@ -275,7 +240,6 @@ impl Layer { num_solids += 1; } - #[cfg(not(target_arch = "wasm32"))] Mesh::Gradient { .. } => { if last_is_solid.unwrap_or(true) { render_pass.set_pipeline(&gradient.pipeline); @@ -297,11 +261,9 @@ impl Layer { self.gradient.vertices.slice_from_index(num_gradients), ); - num_gradients += 1; - } - #[cfg(target_arch = "wasm32")] - Mesh::Gradient { .. } => {} - }; + num_gradients += 1; + } + }; render_pass.set_index_buffer( self.index_buffer.slice_from_index(index), @@ -460,6 +422,19 @@ fn multisample_state( } } +#[derive(Debug, Clone, Copy, encase::ShaderType)] +pub struct Uniforms { + transform: glam::Mat4, +} + +impl Uniforms { + pub fn new(transform: Transformation) -> Self { + Self { + transform: transform.into(), + } + } +} + mod solid { use crate::buffer::dynamic; use crate::buffer::r#static::Buffer; @@ -530,19 +505,6 @@ mod solid { } } - #[derive(Debug, Clone, Copy, ShaderType)] - pub struct Uniforms { - transform: glam::Mat4, - } - - impl Uniforms { - pub fn new(transform: Transformation) -> Self { - Self { - transform: transform.into(), - } - } - } - impl Pipeline { /// Creates a new [SolidPipeline] using `solid.wgsl` shader. pub fn new( @@ -627,14 +589,11 @@ mod solid { } } -#[cfg(not(target_arch = "wasm32"))] mod gradient { - use crate::buffer::dynamic; use crate::buffer::r#static::Buffer; use crate::graphics::Antialiasing; use crate::triangle; - use encase::ShaderType; use glam::{IVec4, Vec4}; use iced_graphics::primitive; @@ -646,14 +605,9 @@ mod gradient { #[derive(Debug)] pub struct Layer { - pub vertices: Buffer, - pub uniforms: dynamic::Buffer, - pub storage: dynamic::Buffer, + pub vertices: Buffer, + pub uniforms: dynamic::Buffer, //TODO(shan) pub constants: wgpu::BindGroup, - pub color_stop_offset: i32, - //Need to store these and then write them all at once - //or else they will be padded to 256 and cause gaps in the storage buffer - pub color_stops_pending_write: Storage, } impl Layer { @@ -672,13 +626,6 @@ mod gradient { "iced_wgpu::triangle::gradient uniforms", ); - // Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static - // sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work - let storage = dynamic::Buffer::storage( - device, - "iced_wgpu::triangle::gradient storage", - ); - let constants = Self::bind_group( device, uniforms.raw(), @@ -727,27 +674,6 @@ mod gradient { } } - #[derive(Debug, ShaderType)] - pub struct Uniforms { - pub transform: glam::Mat4, - //xy = start, zw = end - pub direction: Vec4, - //x = start stop, y = end stop, zw = padding - pub stop_range: IVec4, - } - - #[derive(Debug, ShaderType)] - pub struct ColorStop { - pub color: Vec4, - pub offset: f32, - } - - #[derive(Debug, ShaderType)] - pub struct Storage { - #[size(runtime)] - pub color_stops: Vec, - } - impl Pipeline { /// Creates a new [GradientPipeline] using `gradient.wgsl` shader. pub(super) fn new( @@ -760,30 +686,16 @@ mod gradient { label: Some( "iced_wgpu::triangle::gradient bind group layout", ), - entries: &[ - wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }, - wgpu::BindGroupLayoutEntry { - binding: 1, - visibility: wgpu::ShaderStages::FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Storage { - read_only: true, - }, - has_dynamic_offset: false, - min_binding_size: Some(Storage::min_size()), - }, - count: None, + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: Some(Uniforms::min_size()), }, - ], + count: None, + }], }, ); @@ -818,16 +730,34 @@ mod gradient { module: &shader, entry_point: "vs_main", buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::< - primitive::Vertex2D, - >( - ) - as u64, + array_stride: std::mem::size_of::() as u64, step_mode: wgpu::VertexStepMode::Vertex, attributes: &wgpu::vertex_attr_array!( - // Position - 0 => Float32x2, - ), + // Position + 0 => Float32x2, + // Color 1 + 1 => Float32x4, + // Color 2 + 2 => Float32x4, + // Color 3 + 3 => Float32x4, + // Color 4 + 4 => Float32x4, + // Color 5 + 5 => Float32x4, + // Color 6 + 6 => Float32x4, + // Color 7 + 7 => Float32x4, + // Color 8 + 8 => Float32x4, + // Offsets 1-4 + 9 => Float32x4, + // Offsets 5-8 + 10 => Float32x4, + // Direction + 11 => Float32x4 + ), }], }, fragment: Some(wgpu::FragmentState { From 9534712dd0b214aace7dad671a871cfb97131524 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Thu, 22 Dec 2022 07:59:33 -0800 Subject: [PATCH 2/7] - Changed syntaxes & naming conventions re: PR comments. - Adjusted default instances/triangles to be more consistent. --- core/src/angle.rs | 2 +- core/src/gradient.rs | 4 +- examples/tour/src/main.rs | 23 +- style/src/button.rs | 2 +- style/src/theme.rs | 2 +- wgpu/src/geometry.rs | 4 +- wgpu/src/layer.rs | 2 +- wgpu/src/quad.rs | 272 ++++++++++++++++++ .../{triangle/gradient.wgsl => triangle.wgsl} | 31 +- wgpu/src/shader/triangle/solid.wgsl | 30 -- wgpu/src/triangle.rs | 59 +--- 11 files changed, 326 insertions(+), 105 deletions(-) rename wgpu/src/shader/{triangle/gradient.wgsl => triangle.wgsl} (84%) delete mode 100644 wgpu/src/shader/triangle/solid.wgsl diff --git a/core/src/angle.rs b/core/src/angle.rs index 167cc5ed3d..04ca50ffde 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -10,6 +10,6 @@ pub struct Radians(pub f32); impl From for Radians { fn from(degrees: Degrees) -> Self { - Radians(degrees.0 * (PI / 180.0)) + Radians(degrees.0 * PI / 180.0) } } diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 503d757ae5..843da108fd 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -23,7 +23,7 @@ impl Gradient { } /// Adjust the opacity of the gradient by a multiplier applied to each color stop. - pub fn adjust_alpha(mut self, alpha_multiplier: f32) -> Self { + pub fn transparentize(mut self, alpha_multiplier: f32) -> Self { match &mut self { Gradient::Linear(linear) => { for stop in &mut linear.color_stops { @@ -36,7 +36,7 @@ impl Gradient { } /// Packs the [`Gradient`] into a buffer for use in shader code. - pub fn flatten(&self, bounds: Rectangle) -> [f32; 44] { + pub fn pack(&self, bounds: Rectangle) -> [f32; 44] { match self { Gradient::Linear(linear) => { let mut pack: [f32; 44] = [0.0; 44]; diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 878667f43e..3b86ec4135 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -1,5 +1,4 @@ -use iced::theme; -use iced::theme::Palette; +use iced::theme::{self, Palette}; use iced::widget::{ checkbox, column, container, horizontal_space, image, radio, row, scrollable, slider, text, text_input, toggler, vertical_space, @@ -55,7 +54,9 @@ impl Sandbox for Tour { if steps.has_previous() { controls = controls.push( button("Back").on_press(Message::BackPressed).style( - theme::Button::Custom(Box::new(CustomButtonStyle::Danger)), + theme::Button::Custom(Box::new( + CustomButtonStyle::Secondary, + )), ), ); } @@ -65,7 +66,7 @@ impl Sandbox for Tour { if steps.can_continue() { controls = controls.push( button("Next").on_press(Message::NextPressed).style( - theme::Button::Custom(Box::new(CustomButtonStyle::Success)), + theme::Button::Custom(Box::new(CustomButtonStyle::Primary)), ), ); } @@ -673,8 +674,8 @@ pub enum Layout { } enum CustomButtonStyle { - Success, - Danger, + Primary, + Secondary, } impl widget::button::StyleSheet for CustomButtonStyle { @@ -682,7 +683,7 @@ impl widget::button::StyleSheet for CustomButtonStyle { fn active(&self, _style: &Self::Style) -> widget::button::Appearance { match self { - CustomButtonStyle::Success => widget::button::Appearance { + CustomButtonStyle::Primary => widget::button::Appearance { background: Gradient::linear(Degrees(270.0)) .add_stop(0.0, Palette::LIGHT.primary) .add_stop(1.0, Color::from_rgb8(54, 80, 168)) @@ -690,18 +691,20 @@ impl widget::button::StyleSheet for CustomButtonStyle { .expect("Build gradient") .into(), text_color: Color::WHITE, + border_radius: 5.0, ..Default::default() }, - CustomButtonStyle::Danger => widget::button::Appearance { + CustomButtonStyle::Secondary => widget::button::Appearance { background: Gradient::linear(Radians( 3.0 * std::f32::consts::PI / 2.0, )) - .add_stop(0.0, Palette::LIGHT.danger) - .add_stop(1.0, Color::from_rgb8(125, 26, 24)) + .add_stop(0.0, Color::from_rgb8(194, 194, 194)) + .add_stop(1.0, Color::from_rgb8(126, 126, 126)) .build() .expect("Build gradient") .into(), text_color: Color::WHITE, + border_radius: 5.0, ..Default::default() }, } diff --git a/style/src/button.rs b/style/src/button.rs index ffe755cb54..31570af052 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -69,7 +69,7 @@ pub trait StyleSheet { ..color }), Background::Gradient(gradient) => { - Background::Gradient(gradient.adjust_alpha(0.5)) + Background::Gradient(gradient.transparentize(0.5)) } }), text_color: Color { diff --git a/style/src/theme.rs b/style/src/theme.rs index fbc3f38663..6993e415f1 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -218,7 +218,7 @@ impl button::StyleSheet for Theme { ..color }), Background::Gradient(gradient) => { - Background::Gradient(gradient.adjust_alpha(0.5)) + Background::Gradient(gradient.transparentize(0.5)) } }), text_color: Color { diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index db5c43b858..ab795ab8b6 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -501,7 +501,7 @@ impl tessellation::FillVertexConstructor primitive::GradientVertex2D { position: [position.x, position.y], - gradient: self.gradient.flatten(), + gradient: self.gradient.pack(), } } } @@ -517,7 +517,7 @@ impl tessellation::StrokeVertexConstructor triangle::GradientVertex2D { position: [position.x, position.y], - gradient: self.gradient.flatten(), + gradient: self.gradient.pack(), } } } diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index d422aed8aa..29e59ed045 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -175,7 +175,7 @@ impl<'a> Layer<'a> { } Background::Gradient(gradient) => { let quad = quad::Gradient { - gradient: (*gradient).flatten(Rectangle::new( + gradient: (*gradient).pack(Rectangle::new( properties.position.into(), properties.size.into(), )), diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 2be21d2d77..04301bd60e 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -327,3 +327,275 @@ impl Default for Uniforms { } } } + +// mod solid { +// use crate::buffer; +// use crate::quad::{QuadVertex, INITIAL_INSTANCES}; +// use iced_graphics::layer::quad; +// use std::mem; +// +// #[derive(Debug)] +// pub struct Pipeline { +// pub pipeline: wgpu::RenderPipeline, +// pub instances: buffer::Static, +// } +// +// impl Pipeline { +// pub fn new( +// device: &wgpu::Device, +// format: wgpu::TextureFormat, +// uniforms_layout: &wgpu::BindGroupLayout, +// ) -> Self { +// let instances = buffer::Static::new( +// device, +// "iced_wgpu::quad::solid instance buffer", +// wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, +// INITIAL_INSTANCES, +// ); +// +// let layout = device.create_pipeline_layout( +// &wgpu::PipelineLayoutDescriptor { +// label: Some("iced_wgpu::quad::solid pipeline layout"), +// push_constant_ranges: &[], +// bind_group_layouts: &[uniforms_layout], +// }, +// ); +// +// let shader = +// device.create_shader_module(wgpu::ShaderModuleDescriptor { +// label: Some("iced_wgpu::quad::solid shader"), +// source: wgpu::ShaderSource::Wgsl( +// std::borrow::Cow::Borrowed(include_str!( +// "shader/quad.wgsl" +// )), +// ), +// }); +// +// let pipeline = device.create_render_pipeline( +// &wgpu::RenderPipelineDescriptor { +// label: Some("iced_wgpu::quad::solid pipeline"), +// layout: Some(&layout), +// vertex: wgpu::VertexState { +// module: &shader, +// entry_point: "solid_vs_main", +// buffers: &[ +// wgpu::VertexBufferLayout { +// array_stride: mem::size_of::() +// as u64, +// step_mode: wgpu::VertexStepMode::Vertex, +// attributes: &[wgpu::VertexAttribute { +// shader_location: 0, +// format: wgpu::VertexFormat::Float32x2, +// offset: 0, +// }], +// }, +// wgpu::VertexBufferLayout { +// array_stride: mem::size_of::() +// as u64, +// step_mode: wgpu::VertexStepMode::Instance, +// attributes: &wgpu::vertex_attr_array!( +// // Color +// 1 => Float32x4, +// // Position +// 2 => Float32x2, +// // Size +// 3 => Float32x2, +// // Border color +// 4 => Float32x4, +// // Border radius +// 5 => Float32x4, +// // Border width +// 6 => Float32, +// ), +// }, +// ], +// }, +// fragment: Some(wgpu::FragmentState { +// module: &shader, +// entry_point: "solid_fs_main", +// targets: &[Some(wgpu::ColorTargetState { +// format, +// blend: Some(wgpu::BlendState { +// color: wgpu::BlendComponent { +// src_factor: wgpu::BlendFactor::SrcAlpha, +// dst_factor: +// wgpu::BlendFactor::OneMinusSrcAlpha, +// operation: wgpu::BlendOperation::Add, +// }, +// alpha: wgpu::BlendComponent { +// src_factor: wgpu::BlendFactor::One, +// dst_factor: +// wgpu::BlendFactor::OneMinusSrcAlpha, +// operation: wgpu::BlendOperation::Add, +// }, +// }), +// write_mask: wgpu::ColorWrites::ALL, +// })], +// }), +// primitive: wgpu::PrimitiveState { +// topology: wgpu::PrimitiveTopology::TriangleList, +// front_face: wgpu::FrontFace::Cw, +// ..Default::default() +// }, +// depth_stencil: None, +// multisample: wgpu::MultisampleState { +// count: 1, +// mask: !0, +// alpha_to_coverage_enabled: false, +// }, +// multiview: None, +// }, +// ); +// +// Self { +// pipeline, +// instances, +// } +// } +// } +// } +// +// mod gradient { +// use crate::buffer; +// use crate::quad::{QuadVertex, INITIAL_INSTANCES}; +// use iced_graphics::layer::quad; +// use std::mem; +// +// #[derive(Debug)] +// pub struct Pipeline { +// pub pipeline: wgpu::RenderPipeline, +// pub instances: buffer::Static, +// } +// +// impl Pipeline { +// pub fn new( +// device: &wgpu::Device, +// format: wgpu::TextureFormat, +// uniforms_layout: &wgpu::BindGroupLayout, +// ) -> Self { +// let instances = buffer::Static::new( +// device, +// "iced_wgpu::quad::gradient instance buffer", +// wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, +// INITIAL_INSTANCES, +// ); +// +// let layout = device.create_pipeline_layout( +// &wgpu::PipelineLayoutDescriptor { +// label: Some("iced_wgpu::quad::gradient pipeline layout"), +// push_constant_ranges: &[], +// bind_group_layouts: &[uniforms_layout], +// }, +// ); +// +// let shader = +// device.create_shader_module(wgpu::ShaderModuleDescriptor { +// label: Some("iced_wgpu::quad::gradient shader"), +// source: wgpu::ShaderSource::Wgsl( +// std::borrow::Cow::Borrowed(include_str!( +// "shader/quad.wgsl" +// )), +// ), +// }); +// +// let pipeline = device.create_render_pipeline( +// &wgpu::RenderPipelineDescriptor { +// label: Some("iced_wgpu::quad::gradient pipeline"), +// layout: Some(&layout), +// vertex: wgpu::VertexState { +// module: &shader, +// entry_point: "gradient_vs_main", +// buffers: &[ +// wgpu::VertexBufferLayout { +// array_stride: mem::size_of::() +// as u64, +// step_mode: wgpu::VertexStepMode::Vertex, +// attributes: &[wgpu::VertexAttribute { +// shader_location: 0, +// format: wgpu::VertexFormat::Float32x2, +// offset: 0, +// }], +// }, +// wgpu::VertexBufferLayout { +// array_stride: mem::size_of::() +// as u64, +// step_mode: wgpu::VertexStepMode::Instance, +// attributes: &wgpu::vertex_attr_array!( +// // Color 1 +// 1 => Float32x4, +// // Color 2 +// 2 => Float32x4, +// // Color 3 +// 3 => Float32x4, +// // Color 4 +// 4 => Float32x4, +// // Color 5 +// 5 => Float32x4, +// // Color 6 +// 6 => Float32x4, +// // Color 7 +// 7 => Float32x4, +// // Color 8 +// 8 => Float32x4, +// // Offsets 1-4 +// 9 => Float32x4, +// // Offsets 5-8 +// 10 => Float32x4, +// // Direction +// 11 => Float32x4, +// // Position & Scale +// 12 => Float32x4, +// // Border color +// 13 => Float32x4, +// // Border radius +// 14 => Float32x4, +// // Border width +// 15 => Float32 +// ), +// }, +// ], +// }, +// fragment: Some(wgpu::FragmentState { +// module: &shader, +// entry_point: "gradient_fs_main", +// targets: &[Some(wgpu::ColorTargetState { +// format, +// blend: Some(wgpu::BlendState { +// color: wgpu::BlendComponent { +// src_factor: wgpu::BlendFactor::SrcAlpha, +// dst_factor: +// wgpu::BlendFactor::OneMinusSrcAlpha, +// operation: wgpu::BlendOperation::Add, +// }, +// alpha: wgpu::BlendComponent { +// src_factor: wgpu::BlendFactor::One, +// dst_factor: +// wgpu::BlendFactor::OneMinusSrcAlpha, +// operation: wgpu::BlendOperation::Add, +// }, +// }), +// write_mask: wgpu::ColorWrites::ALL, +// })], +// }), +// primitive: wgpu::PrimitiveState { +// topology: wgpu::PrimitiveTopology::TriangleList, +// front_face: wgpu::FrontFace::Cw, +// ..Default::default() +// }, +// depth_stencil: None, +// multisample: wgpu::MultisampleState { +// count: 1, +// mask: !0, +// alpha_to_coverage_enabled: false, +// }, +// multiview: None, +// }, +// ); +// +// Self { +// pipeline, +// instances, +// } +// } +// } +// } \ No newline at end of file diff --git a/wgpu/src/shader/triangle/gradient.wgsl b/wgpu/src/shader/triangle.wgsl similarity index 84% rename from wgpu/src/shader/triangle/gradient.wgsl rename to wgpu/src/shader/triangle.wgsl index 24c330a2e5..71c9fb9a40 100644 --- a/wgpu/src/shader/triangle/gradient.wgsl +++ b/wgpu/src/shader/triangle.wgsl @@ -4,6 +4,31 @@ struct Globals { @group(0) @binding(0) var globals: Globals; +struct SolidVertexInput { + @location(0) position: vec2, + @location(1) color: vec4, +} + +struct SolidVertexOutput { + @builtin(position) position: vec4, + @location(0) color: vec4, +} + +@vertex +fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput { + var out: SolidVertexOutput; + + out.color = input.color; + out.position = globals.transform * vec4(input.position, 0.0, 1.0); + + return out; +} + +@fragment +fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4 { + return input.color; +} + struct GradientVertexOutput { @builtin(position) position: vec4, @location(0) raw_position: vec2, @@ -21,7 +46,7 @@ struct GradientVertexOutput { } @vertex -fn vs_main( +fn gradient_vs_main( @location(0) input: vec2, @location(1) color_1: vec4, @location(2) color_2: vec4, @@ -39,7 +64,6 @@ fn vs_main( output.position = globals.transform * vec4(input.xy, 0.0, 1.0); output.raw_position = input; - //pass gradient data to frag shader output.color_1 = color_1; output.color_2 = color_2; output.color_3 = color_3; @@ -103,7 +127,7 @@ fn gradient( } @fragment -fn fs_main(input: GradientVertexOutput) -> @location(0) vec4 { +fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( input.color_1, input.color_2, @@ -126,7 +150,6 @@ fn fs_main(input: GradientVertexOutput) -> @location(0) vec4 { input.offsets_2.w, ); - //TODO could just pass this in to the shader but is probably more performant to just check it here var last_index = 7; for (var i: i32 = 0; i <= 7; i++) { if (offsets[i] >= 1.0) { diff --git a/wgpu/src/shader/triangle/solid.wgsl b/wgpu/src/shader/triangle/solid.wgsl deleted file mode 100644 index 808f7eaa60..0000000000 --- a/wgpu/src/shader/triangle/solid.wgsl +++ /dev/null @@ -1,30 +0,0 @@ -struct Globals { - transform: mat4x4, -} - -@group(0) @binding(0) var globals: Globals; - -struct SolidVertexInput { - @location(0) position: vec2, - @location(1) color: vec4, -} - -struct SolidVertexOutput { - @builtin(position) position: vec4, - @location(0) color: vec4, -} - -@vertex -fn vs_main(input: SolidVertexInput) -> SolidVertexOutput { - var out: SolidVertexOutput; - - out.color = input.color; - out.position = globals.transform * vec4(input.position, 0.0, 1.0); - - return out; -} - -@fragment -fn fs_main(input: SolidVertexOutput) -> @location(0) vec4 { - return input.color; -} diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 8bee712cb5..95da0c2db5 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -114,53 +114,6 @@ impl Layer { ); gradient_vertex_offset += written_bytes; - - match gradient { - Gradient::Linear(linear) => { - use glam::{IVec4, Vec4}; - - let start_offset = self.gradient.color_stop_offset; - let end_offset = (linear.color_stops.len() as i32) - + start_offset - - 1; - - self.gradient.uniforms.push(&gradient::Uniforms { - transform: transform.into(), - direction: Vec4::new( - linear.start.x, - linear.start.y, - linear.end.x, - linear.end.y, - ), - stop_range: IVec4::new( - start_offset, - end_offset, - 0, - 0, - ), - }); - - self.gradient.color_stop_offset = end_offset + 1; - - let stops: Vec = linear - .color_stops - .iter() - .map(|stop| { - let [r, g, b, a] = stop.color.into_linear(); - - gradient::ColorStop { - offset: stop.offset, - color: Vec4::new(r, g, b, a), - } - }) - .collect(); - - self.gradient - .color_stops_pending_write - .color_stops - .extend(stops); - } - } } } } @@ -543,7 +496,7 @@ mod solid { ), source: wgpu::ShaderSource::Wgsl( std::borrow::Cow::Borrowed(include_str!( - "shader/solid.wgsl" + "shader/triangle.wgsl" )), ), }); @@ -554,7 +507,7 @@ mod solid { layout: Some(&layout), vertex: wgpu::VertexState { module: &shader, - entry_point: "vs_main", + entry_point: "solid_vs_main", buffers: &[wgpu::VertexBufferLayout { array_stride: std::mem::size_of::< primitive::ColoredVertex2D, @@ -571,7 +524,7 @@ mod solid { }, fragment: Some(wgpu::FragmentState { module: &shader, - entry_point: "fs_main", + entry_point: "solid_fs_main", targets: &[triangle::fragment_target(format)], }), primitive: triangle::primitive_state(), @@ -716,7 +669,7 @@ mod gradient { ), source: wgpu::ShaderSource::Wgsl( std::borrow::Cow::Borrowed(include_str!( - "shader/gradient.wgsl" + "shader/triangle.wgsl" )), ), }); @@ -728,7 +681,7 @@ mod gradient { layout: Some(&layout), vertex: wgpu::VertexState { module: &shader, - entry_point: "vs_main", + entry_point: "gradient_vs_main", buffers: &[wgpu::VertexBufferLayout { array_stride: std::mem::size_of::() as u64, step_mode: wgpu::VertexStepMode::Vertex, @@ -762,7 +715,7 @@ mod gradient { }, fragment: Some(wgpu::FragmentState { module: &shader, - entry_point: "fs_main", + entry_point: "gradient_fs_main", targets: &[triangle::fragment_target(format)], }), primitive: triangle::primitive_state(), From e03aee8d20f1cbde12302f9bc1f9bb4a246f5317 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Tue, 10 Jan 2023 10:25:42 -0800 Subject: [PATCH 3/7] Removed preprocessor and just duping shader code (for simplicity, for now!) --- examples/solar_system/Cargo.toml | 1 + examples/solar_system/src/main.rs | 2 + glow/src/shader/includes/border_radius.vert | 9 -- glow/src/shader/includes/gradient.frag | 61 ------------- glow/src/shader/includes/quad_distance.vert | 17 ---- .../shader/quad/compatibility/gradient.frag | 90 ++++++++++++++++++- glow/src/shader/quad/compatibility/solid.frag | 28 +++++- glow/src/shader/quad/core/gradient.frag | 90 ++++++++++++++++++- glow/src/shader/quad/core/solid.frag | 28 +++++- glow/src/shader/triangle/gradient.frag | 62 ++++++++++++- 10 files changed, 296 insertions(+), 92 deletions(-) delete mode 100644 glow/src/shader/includes/border_radius.vert delete mode 100644 glow/src/shader/includes/gradient.frag delete mode 100644 glow/src/shader/includes/quad_distance.vert diff --git a/examples/solar_system/Cargo.toml b/examples/solar_system/Cargo.toml index 835396b048..1a98a87e70 100644 --- a/examples/solar_system/Cargo.toml +++ b/examples/solar_system/Cargo.toml @@ -7,4 +7,5 @@ publish = false [dependencies] iced = { path = "../..", features = ["canvas", "tokio", "debug"] } +env_logger = "0.10.0" rand = "0.8.3" diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index 5494ce4bfd..c09cd8cfbc 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -21,6 +21,8 @@ use iced::{ use std::time::Instant; pub fn main() -> iced::Result { + env_logger::builder().format_timestamp(None).init(); + SolarSystem::run(Settings { antialiasing: true, ..Settings::default() diff --git a/glow/src/shader/includes/border_radius.vert b/glow/src/shader/includes/border_radius.vert deleted file mode 100644 index 891c65e220..0000000000 --- a/glow/src/shader/includes/border_radius.vert +++ /dev/null @@ -1,9 +0,0 @@ -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} diff --git a/glow/src/shader/includes/gradient.frag b/glow/src/shader/includes/gradient.frag deleted file mode 100644 index ff16ecbff6..0000000000 --- a/glow/src/shader/includes/gradient.frag +++ /dev/null @@ -1,61 +0,0 @@ -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color; -} diff --git a/glow/src/shader/includes/quad_distance.vert b/glow/src/shader/includes/quad_distance.vert deleted file mode 100644 index 54e648148f..0000000000 --- a/glow/src/shader/includes/quad_distance.vert +++ /dev/null @@ -1,17 +0,0 @@ -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} diff --git a/glow/src/shader/quad/compatibility/gradient.frag b/glow/src/shader/quad/compatibility/gradient.frag index 1f79b1cd75..ed99121b6c 100644 --- a/glow/src/shader/quad/compatibility/gradient.frag +++ b/glow/src/shader/quad/compatibility/gradient.frag @@ -6,7 +6,95 @@ precision mediump float; #endif #endif -// #includes +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + +vec4 gradient( + vec4 direction, + vec2 raw_position, + vec4 offsets[2], + vec4 colors[8] +) { + vec2 start = direction.xy; + vec2 end = direction.zw; + + vec2 v1 = vec2(end - start); + vec2 v2 = vec2(raw_position - start); + vec2 unit = normalize(v1); + float coord_offset = dot(unit, v2) / length(v1); + + //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill + vec4 color; + + float offsets_arr[8]; + offsets_arr[0] = offsets[0].x; + offsets_arr[1] = offsets[0].y; + offsets_arr[2] = offsets[0].z; + offsets_arr[3] = offsets[0].w; + offsets_arr[4] = offsets[1].x; + offsets_arr[5] = offsets[1].y; + offsets_arr[6] = offsets[1].z; + offsets_arr[7] = offsets[1].w; + + int last_index = 7; + for (int i = 0; i <= 7; i++) { + if (offsets_arr[i] > 1.0) { + last_index = i - 1; + break; + } + } + + for (int i = 0; i < last_index; i++) { + float curr_offset = offsets_arr[i]; + float next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + //current coordinate is before the first defined offset, set it to the start color + color = colors[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + //current fragment is between the current offset & the next one, interpolate colors + color = mix(colors[i], colors[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + //current coordinate is after the last defined offset, set it to the last color + color = colors[last_index]; + } + } + + return color; +} + +float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) +{ + // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN + vec2 inner_size = size - vec2(radius, radius) * 2.0; + vec2 top_left = position + vec2(radius, radius); + vec2 bottom_right = top_left + inner_size; + + vec2 top_left_distance = top_left - frag_coord; + vec2 bottom_right_distance = frag_coord - bottom_right; + + vec2 distance = vec2( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(distance.x * distance.x + distance.y * distance.y); +} uniform float u_screen_height; diff --git a/glow/src/shader/quad/compatibility/solid.frag b/glow/src/shader/quad/compatibility/solid.frag index 202ec9c620..42078b3513 100644 --- a/glow/src/shader/quad/compatibility/solid.frag +++ b/glow/src/shader/quad/compatibility/solid.frag @@ -6,7 +6,33 @@ precision mediump float; #endif #endif -// #includes +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + +float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) +{ + // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN + vec2 inner_size = size - vec2(radius, radius) * 2.0; + vec2 top_left = position + vec2(radius, radius); + vec2 bottom_right = top_left + inner_size; + + vec2 top_left_distance = top_left - frag_coord; + vec2 bottom_right_distance = frag_coord - bottom_right; + + vec2 distance = vec2( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(distance.x * distance.x + distance.y * distance.y); +} uniform float u_screen_height; diff --git a/glow/src/shader/quad/core/gradient.frag b/glow/src/shader/quad/core/gradient.frag index 26d85ee0b8..f9de93225e 100644 --- a/glow/src/shader/quad/core/gradient.frag +++ b/glow/src/shader/quad/core/gradient.frag @@ -11,7 +11,95 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif -// #includes +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + +vec4 gradient( + vec4 direction, + vec2 raw_position, + vec4 offsets[2], + vec4 colors[8] +) { + vec2 start = direction.xy; + vec2 end = direction.zw; + + vec2 v1 = vec2(end - start); + vec2 v2 = vec2(raw_position - start); + vec2 unit = normalize(v1); + float coord_offset = dot(unit, v2) / length(v1); + + //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill + vec4 color; + + float offsets_arr[8]; + offsets_arr[0] = offsets[0].x; + offsets_arr[1] = offsets[0].y; + offsets_arr[2] = offsets[0].z; + offsets_arr[3] = offsets[0].w; + offsets_arr[4] = offsets[1].x; + offsets_arr[5] = offsets[1].y; + offsets_arr[6] = offsets[1].z; + offsets_arr[7] = offsets[1].w; + + int last_index = 7; + for (int i = 0; i <= 7; i++) { + if (offsets_arr[i] > 1.0) { + last_index = i - 1; + break; + } + } + + for (int i = 0; i < last_index; i++) { + float curr_offset = offsets_arr[i]; + float next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + //current coordinate is before the first defined offset, set it to the start color + color = colors[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + //current fragment is between the current offset & the next one, interpolate colors + color = mix(colors[i], colors[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + //current coordinate is after the last defined offset, set it to the last color + color = colors[last_index]; + } + } + + return color; +} + +float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) +{ + // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN + vec2 inner_size = size - vec2(radius, radius) * 2.0; + vec2 top_left = position + vec2(radius, radius); + vec2 bottom_right = top_left + inner_size; + + vec2 top_left_distance = top_left - frag_coord; + vec2 bottom_right_distance = frag_coord - bottom_right; + + vec2 distance = vec2( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(distance.x * distance.x + distance.y * distance.y); +} uniform float u_screen_height; diff --git a/glow/src/shader/quad/core/solid.frag b/glow/src/shader/quad/core/solid.frag index 1377e42bd7..ffb3b08ae6 100644 --- a/glow/src/shader/quad/core/solid.frag +++ b/glow/src/shader/quad/core/solid.frag @@ -11,7 +11,33 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif -// #includes +float selectBorderRadius(vec4 radi, vec2 position, vec2 center) +{ + float rx = radi.x; + float ry = radi.y; + rx = position.x > center.x ? radi.y : radi.x; + ry = position.x > center.x ? radi.z : radi.w; + rx = position.y > center.y ? ry : rx; + return rx; +} + +float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) +{ + // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN + vec2 inner_size = size - vec2(radius, radius) * 2.0; + vec2 top_left = position + vec2(radius, radius); + vec2 bottom_right = top_left + inner_size; + + vec2 top_left_distance = top_left - frag_coord; + vec2 bottom_right_distance = frag_coord - bottom_right; + + vec2 distance = vec2( + max(max(top_left_distance.x, bottom_right_distance.x), 0.0), + max(max(top_left_distance.y, bottom_right_distance.y), 0.0) + ); + + return sqrt(distance.x * distance.x + distance.y * distance.y); +} uniform float u_screen_height; diff --git a/glow/src/shader/triangle/gradient.frag b/glow/src/shader/triangle/gradient.frag index 9e56854098..1c7b50a16b 100644 --- a/glow/src/shader/triangle/gradient.frag +++ b/glow/src/shader/triangle/gradient.frag @@ -11,7 +11,67 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif -// #includes +vec4 gradient( + vec4 direction, + vec2 raw_position, + vec4 offsets[2], + vec4 colors[8] +) { + vec2 start = direction.xy; + vec2 end = direction.zw; + + vec2 v1 = vec2(end - start); + vec2 v2 = vec2(raw_position - start); + vec2 unit = normalize(v1); + float coord_offset = dot(unit, v2) / length(v1); + + //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill + vec4 color; + + float offsets_arr[8]; + offsets_arr[0] = offsets[0].x; + offsets_arr[1] = offsets[0].y; + offsets_arr[2] = offsets[0].z; + offsets_arr[3] = offsets[0].w; + offsets_arr[4] = offsets[1].x; + offsets_arr[5] = offsets[1].y; + offsets_arr[6] = offsets[1].z; + offsets_arr[7] = offsets[1].w; + + int last_index = 7; + for (int i = 0; i <= 7; i++) { + if (offsets_arr[i] > 1.0) { + last_index = i - 1; + break; + } + } + + for (int i = 0; i < last_index; i++) { + float curr_offset = offsets_arr[i]; + float next_offset = offsets_arr[i+1]; + + if (coord_offset <= offsets_arr[0]) { + //current coordinate is before the first defined offset, set it to the start color + color = colors[0]; + } + + if (curr_offset <= coord_offset && coord_offset <= next_offset) { + //current fragment is between the current offset & the next one, interpolate colors + color = mix(colors[i], colors[i+1], smoothstep( + curr_offset, + next_offset, + coord_offset + )); + } + + if (coord_offset >= offsets_arr[last_index]) { + //current coordinate is after the last defined offset, set it to the last color + color = colors[last_index]; + } + } + + return color; +} in vec2 v_raw_position; in vec4 v_colors[8]; From 403019ec0125fcc1130dba1cc3d9f54c24148c25 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Wed, 11 Jan 2023 14:01:01 -0800 Subject: [PATCH 4/7] Added dithering to fix gradient banding. --- glow/src/shader/quad/compatibility/gradient.frag | 8 +++++++- glow/src/shader/quad/core/gradient.frag | 8 +++++++- glow/src/shader/triangle/gradient.frag | 8 +++++++- wgpu/src/shader/quad.wgsl | 8 +++++++- wgpu/src/shader/triangle.wgsl | 8 +++++++- 5 files changed, 35 insertions(+), 5 deletions(-) diff --git a/glow/src/shader/quad/compatibility/gradient.frag b/glow/src/shader/quad/compatibility/gradient.frag index ed99121b6c..97c40d0942 100644 --- a/glow/src/shader/quad/compatibility/gradient.frag +++ b/glow/src/shader/quad/compatibility/gradient.frag @@ -6,6 +6,10 @@ precision mediump float; #endif #endif +float random(vec2 coords) { + return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); +} + float selectBorderRadius(vec4 radi, vec2 position, vec2 center) { float rx = radi.x; @@ -33,6 +37,8 @@ vec4 gradient( //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill vec4 color; + float noise_granularity = 0.3/255.0; + float offsets_arr[8]; offsets_arr[0] = offsets[0].x; offsets_arr[1] = offsets[0].y; @@ -75,7 +81,7 @@ vec4 gradient( } } - return color; + return color += mix(-noise_granularity, noise_granularity, random(raw_position)); } float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) diff --git a/glow/src/shader/quad/core/gradient.frag b/glow/src/shader/quad/core/gradient.frag index f9de93225e..984c399869 100644 --- a/glow/src/shader/quad/core/gradient.frag +++ b/glow/src/shader/quad/core/gradient.frag @@ -11,6 +11,10 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif +float random(vec2 coords) { + return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); +} + float selectBorderRadius(vec4 radi, vec2 position, vec2 center) { float rx = radi.x; @@ -38,6 +42,8 @@ vec4 gradient( //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill vec4 color; + float noise_granularity = 0.3/255.0; + float offsets_arr[8]; offsets_arr[0] = offsets[0].x; offsets_arr[1] = offsets[0].y; @@ -80,7 +86,7 @@ vec4 gradient( } } - return color; + return color += mix(-noise_granularity, noise_granularity, random(raw_position)); } float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) diff --git a/glow/src/shader/triangle/gradient.frag b/glow/src/shader/triangle/gradient.frag index 1c7b50a16b..435af3d0ee 100644 --- a/glow/src/shader/triangle/gradient.frag +++ b/glow/src/shader/triangle/gradient.frag @@ -11,6 +11,10 @@ out vec4 fragColor; #define gl_FragColor fragColor #endif +float random(vec2 coords) { + return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); +} + vec4 gradient( vec4 direction, vec2 raw_position, @@ -28,6 +32,8 @@ vec4 gradient( //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill vec4 color; + float noise_granularity = 0.3/255.0; + float offsets_arr[8]; offsets_arr[0] = offsets[0].x; offsets_arr[1] = offsets[0].y; @@ -70,7 +76,7 @@ vec4 gradient( } } - return color; + return color += mix(-noise_granularity, noise_granularity, random(raw_position)); } in vec2 v_raw_position; diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 9ef20a66f2..3232bdbeea 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -218,6 +218,10 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { return out; } +fn random(coords: vec2) -> f32 { + return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453); +} + /// Returns the current interpolated color with a max 8-stop gradient fn gradient( raw_position: vec2, @@ -241,6 +245,8 @@ fn gradient( var color: vec4; + let noise_granularity: f32 = 0.3/255.0; + for (var i: i32 = 0; i < last_index; i++) { let curr_offset = offsets_arr[i]; let next_offset = offsets_arr[i+1]; @@ -262,7 +268,7 @@ fn gradient( } } - return color; + return color + mix(-noise_granularity, noise_granularity, random(raw_position)); } @fragment diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl index 71c9fb9a40..625fa46e68 100644 --- a/wgpu/src/shader/triangle.wgsl +++ b/wgpu/src/shader/triangle.wgsl @@ -79,6 +79,10 @@ fn gradient_vs_main( return output; } +fn random(coords: vec2) -> f32 { + return fract(sin(dot(coords, vec2(12.9898,78.233))) * 43758.5453); +} + /// Returns the current interpolated color with a max 8-stop gradient fn gradient( raw_position: vec2, @@ -102,6 +106,8 @@ fn gradient( var color: vec4; + let noise_granularity: f32 = 0.3/255.0; + for (var i: i32 = 0; i < last_index; i++) { let curr_offset = offsets_arr[i]; let next_offset = offsets_arr[i+1]; @@ -123,7 +129,7 @@ fn gradient( } } - return color; + return color + mix(-noise_granularity, noise_granularity, random(raw_position)); } @fragment From f60c065c6c8fe21376d96ad51c64dd1444d29504 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Tue, 14 Mar 2023 20:39:39 -0700 Subject: [PATCH 5/7] Rebased PR to `advanced-text` --- core/Cargo.toml | 3 - core/src/angle.rs | 18 + core/src/gradient.rs | 58 +- core/src/gradient/linear.rs | 0 examples/modern_art/src/main.rs | 0 examples/tour/src/main.rs | 6 +- glow/src/program.rs | 0 glow/src/quad/compatibility.rs | 0 glow/src/quad/core.rs | 0 .../shader/quad/compatibility/gradient.frag | 158 ---- .../shader/quad/compatibility/gradient.vert | 72 -- glow/src/shader/quad/compatibility/solid.frag | 82 -- glow/src/shader/quad/compatibility/solid.vert | 45 -- glow/src/shader/quad/core/gradient.frag | 163 ---- glow/src/shader/quad/core/gradient.vert | 80 -- glow/src/shader/quad/core/solid.frag | 93 --- glow/src/shader/quad/core/solid.vert | 52 -- glow/src/shader/triangle/gradient.frag | 89 --- glow/src/shader/triangle/gradient.vert | 42 - glow/src/shader/triangle/solid.vert | 11 - glow/src/window/compositor.rs | 0 graphics/src/geometry.rs | 2 - graphics/src/geometry/fill.rs | 3 +- graphics/src/geometry/style.rs | 3 +- graphics/src/gradient.rs | 128 +++ graphics/src/lib.rs | 2 + graphics/src/primitive.rs | 25 +- graphics/src/triangle.rs | 1 - src/lib.rs | 4 +- style/src/button.rs | 2 +- style/src/theme.rs | 2 +- tiny_skia/src/backend.rs | 41 +- tiny_skia/src/geometry.rs | 3 +- wgpu/Cargo.toml | 4 - wgpu/src/buffer.rs | 281 +------ wgpu/src/buffer/dynamic.rs | 0 wgpu/src/buffer/static.rs | 107 --- wgpu/src/geometry.rs | 51 +- wgpu/src/image.rs | 46 +- wgpu/src/layer.rs | 46 +- wgpu/src/layer/mesh.rs | 10 +- wgpu/src/lib.rs | 2 - wgpu/src/quad.rs | 754 +++++++++--------- wgpu/src/triangle.rs | 307 ++++--- widget/src/canvas.rs | 1 + 45 files changed, 844 insertions(+), 1953 deletions(-) delete mode 100644 core/src/gradient/linear.rs delete mode 100644 examples/modern_art/src/main.rs delete mode 100644 glow/src/program.rs delete mode 100644 glow/src/quad/compatibility.rs delete mode 100644 glow/src/quad/core.rs delete mode 100644 glow/src/shader/quad/compatibility/gradient.frag delete mode 100644 glow/src/shader/quad/compatibility/gradient.vert delete mode 100644 glow/src/shader/quad/compatibility/solid.frag delete mode 100644 glow/src/shader/quad/compatibility/solid.vert delete mode 100644 glow/src/shader/quad/core/gradient.frag delete mode 100644 glow/src/shader/quad/core/gradient.vert delete mode 100644 glow/src/shader/quad/core/solid.frag delete mode 100644 glow/src/shader/quad/core/solid.vert delete mode 100644 glow/src/shader/triangle/gradient.frag delete mode 100644 glow/src/shader/triangle/gradient.vert delete mode 100644 glow/src/shader/triangle/solid.vert delete mode 100644 glow/src/window/compositor.rs create mode 100644 graphics/src/gradient.rs delete mode 100644 graphics/src/triangle.rs delete mode 100644 wgpu/src/buffer/dynamic.rs delete mode 100644 wgpu/src/buffer/static.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 45c450b3d2..dac3182868 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,8 +16,5 @@ twox-hash = { version = "1.5", default-features = false } version = "0.6" optional = true -[dependencies.thiserror] -version = "1.0.37" - [target.'cfg(target_arch = "wasm32")'.dependencies] instant = "0.1" diff --git a/core/src/angle.rs b/core/src/angle.rs index 04ca50ffde..75a57c7662 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -1,3 +1,4 @@ +use crate::{Point, Rectangle, Vector}; use std::f32::consts::PI; #[derive(Debug, Copy, Clone, PartialEq)] @@ -13,3 +14,20 @@ impl From for Radians { Radians(degrees.0 * PI / 180.0) } } + +impl Radians { + /// Calculates the line in which the [`Angle`] intercepts the `bounds`. + pub fn to_distance(&self, bounds: &Rectangle) -> (Point, Point) { + let v1 = Vector::new(f32::cos(self.0), f32::sin(self.0)); + + let distance_to_rect = f32::min( + f32::abs((bounds.y - bounds.center().y) / v1.y), + f32::abs(((bounds.x + bounds.width) - bounds.center().x) / v1.x), + ); + + let start = bounds.center() + v1 * distance_to_rect; + let end = bounds.center() - v1 * distance_to_rect; + + (start, end) + } +} diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 843da108fd..7e90c2d83f 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -1,7 +1,7 @@ //! For creating a Gradient. pub use linear::Linear; -use crate::{Color, Radians, Rectangle, Vector}; +use crate::{Color, Radians}; #[derive(Debug, Clone, Copy, PartialEq)] /// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), @@ -23,7 +23,7 @@ impl Gradient { } /// Adjust the opacity of the gradient by a multiplier applied to each color stop. - pub fn transparentize(mut self, alpha_multiplier: f32) -> Self { + pub fn mul_alpha(mut self, alpha_multiplier: f32) -> Self { match &mut self { Gradient::Linear(linear) => { for stop in &mut linear.color_stops { @@ -34,60 +34,6 @@ impl Gradient { self } - - /// Packs the [`Gradient`] into a buffer for use in shader code. - pub fn pack(&self, bounds: Rectangle) -> [f32; 44] { - match self { - Gradient::Linear(linear) => { - let mut pack: [f32; 44] = [0.0; 44]; - let mut offsets: [f32; 8] = [2.0; 8]; - - for (index, stop) in - linear.color_stops.iter().enumerate().take(8) - { - let [r, g, b, a] = stop.color.into_linear(); - - pack[(index * 4)] = r; - pack[(index * 4) + 1] = g; - pack[(index * 4) + 2] = b; - pack[(index * 4) + 3] = a; - - offsets[index] = stop.offset; - } - - pack[32] = offsets[0]; - pack[33] = offsets[1]; - pack[34] = offsets[2]; - pack[35] = offsets[3]; - pack[36] = offsets[4]; - pack[37] = offsets[5]; - pack[38] = offsets[6]; - pack[39] = offsets[7]; - - let v1 = Vector::new( - f32::cos(linear.angle.0), - f32::sin(linear.angle.0), - ); - - let distance_to_rect = f32::min( - f32::abs((bounds.y - bounds.center().y) / v1.y), - f32::abs( - ((bounds.x + bounds.width) - bounds.center().x) / v1.x, - ), - ); - - let start = bounds.center() + v1 * distance_to_rect; - let end = bounds.center() - v1 * distance_to_rect; - - pack[40] = start.x; - pack[41] = start.y; - pack[42] = end.x; - pack[43] = end.y; - - pack - } - } - } } #[derive(Debug, Default, Clone, Copy, PartialEq)] diff --git a/core/src/gradient/linear.rs b/core/src/gradient/linear.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 3b86ec4135..13a84ec6f6 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -4,8 +4,10 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::{alignment, widget, Degrees, Gradient, Radians, Theme}; -use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +use iced::{ + alignment, widget, Color, Degrees, Element, Gradient, Length, Radians, + Renderer, Sandbox, Settings, Theme, +}; pub fn main() -> iced::Result { env_logger::init(); diff --git a/glow/src/program.rs b/glow/src/program.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/glow/src/shader/quad/compatibility/gradient.frag b/glow/src/shader/quad/compatibility/gradient.frag deleted file mode 100644 index 97c40d0942..0000000000 --- a/glow/src/shader/quad/compatibility/gradient.frag +++ /dev/null @@ -1,158 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -float random(vec2 coords) { - return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float noise_granularity = 0.3/255.0; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color += mix(-noise_granularity, noise_granularity, random(raw_position)); -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -varying vec4 v_colors[8]; -varying vec4 v_offsets[2]; -varying vec4 v_direction; -varying vec4 v_position_and_size; -varying vec4 v_border_color; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - vec2 v_position = v_position_and_size.xy; - vec2 v_size = v_position_and_size.zw; - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_size * 0.5).xy - ); - - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_size - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - vec4 mixed_color = mix( - gradient(v_direction, fragCoord, v_offsets, v_colors), - v_border_color, - border_mix - ); - - float d = fDistance( - fragCoord, - v_position, - v_size, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/compatibility/gradient.vert b/glow/src/shader/quad/compatibility/gradient.vert deleted file mode 100644 index 49696be2a7..0000000000 --- a/glow/src/shader/quad/compatibility/gradient.vert +++ /dev/null @@ -1,72 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -//gradient -attribute vec4 i_colors_1; -attribute vec4 i_colors_2; -attribute vec4 i_colors_3; -attribute vec4 i_colors_4; -attribute vec4 i_colors_5; -attribute vec4 i_colors_6; -attribute vec4 i_colors_7; -attribute vec4 i_colors_8; -attribute vec4 i_offsets_1; -attribute vec4 i_offsets_2; -attribute vec4 i_direction; -//quad properties -attribute vec4 i_position_and_size; -attribute vec4 i_border_color; -attribute vec4 i_border_radius; -attribute float i_border_width; -attribute vec2 i_quad_position; - -varying vec4 v_colors[8]; -varying vec4 v_offsets[2]; -varying vec4 v_direction; -varying vec4 v_position_and_size; -varying vec4 v_border_color; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 i_position = i_position_and_size.xy; - vec2 i_size = i_position_and_size.zw; - - vec2 p_position = i_position * u_scale; - vec2 p_size = i_size * u_scale; - - vec4 i_border_radius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_size.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - // array initializers are not supported in GLSL 1.0 (ES 2.0) - v_colors[0] = i_colors_1; - v_colors[1] = i_colors_2; - v_colors[2] = i_colors_3; - v_colors[3] = i_colors_4; - v_colors[4] = i_colors_5; - v_colors[5] = i_colors_6; - v_colors[6] = i_colors_7; - v_colors[7] = i_colors_8; - - v_offsets[0] = i_offsets_1; - v_offsets[1] = i_offsets_2; - - v_direction = i_direction * u_scale; - v_position_and_size = vec4(p_position, p_size); - v_border_color = i_border_color; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(i_quad_position, 0.0, 1.0); -} diff --git a/glow/src/shader/quad/compatibility/solid.frag b/glow/src/shader/quad/compatibility/solid.frag deleted file mode 100644 index 42078b3513..0000000000 --- a/glow/src/shader/quad/compatibility/solid.frag +++ /dev/null @@ -1,82 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -varying vec4 v_color; -varying vec4 v_border_color; -varying vec2 v_position; -varying vec2 v_size; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_size * 0.5).xy - ); - - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_size - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - vec4 mixed_color = mix(v_color, v_border_color, border_mix); - - float d = fDistance( - fragCoord, - v_position, - v_size, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/compatibility/solid.vert b/glow/src/shader/quad/compatibility/solid.vert deleted file mode 100644 index bbac512eb6..0000000000 --- a/glow/src/shader/quad/compatibility/solid.vert +++ /dev/null @@ -1,45 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -attribute vec4 i_color; -attribute vec2 i_position; -attribute vec2 i_size; -attribute vec4 i_border_color; -attribute vec4 i_border_radius; -attribute float i_border_width; -attribute vec2 i_quad_position; - -varying vec4 v_color; -varying vec2 v_position; -varying vec2 v_size; -varying vec4 v_border_color; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 p_position = i_position * u_scale; - vec2 p_size = i_size * u_scale; - - vec4 i_border_radius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_size.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - v_color = i_color; - v_border_color = i_border_color; - v_position = p_position; - v_size = p_size; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(i_quad_position, 0.0, 1.0); -} diff --git a/glow/src/shader/quad/core/gradient.frag b/glow/src/shader/quad/core/gradient.frag deleted file mode 100644 index 984c399869..0000000000 --- a/glow/src/shader/quad/core/gradient.frag +++ /dev/null @@ -1,163 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -#ifdef HIGHER_THAN_300 -out vec4 fragColor; -#define gl_FragColor fragColor -#endif - -float random(vec2 coords) { - return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float noise_granularity = 0.3/255.0; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color += mix(-noise_granularity, noise_granularity, random(raw_position)); -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -in vec4 v_colors[8]; -in vec4 v_offsets[2]; -in vec4 v_direction; -in vec4 v_position_and_size; -in vec4 v_border_color; -in vec4 v_border_radius; -in float v_border_width; - -void main() { - vec2 v_position = v_position_and_size.xy; - vec2 v_size = v_position_and_size.zw; - - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - vec4 mixed_color = gradient(v_direction, fragCoord, v_offsets, v_colors); - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_size * 0.5).xy - ); - - if (v_border_width > 0.0) { - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_size - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - mixed_color = mix(mixed_color, v_border_color, border_mix); - } - - float d = fDistance( - fragCoord, - v_position, - v_size, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/core/gradient.vert b/glow/src/shader/quad/core/gradient.vert deleted file mode 100644 index c003889370..0000000000 --- a/glow/src/shader/quad/core/gradient.vert +++ /dev/null @@ -1,80 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -in vec4 i_colors_1; -in vec4 i_colors_2; -in vec4 i_colors_3; -in vec4 i_colors_4; -in vec4 i_colors_5; -in vec4 i_colors_6; -in vec4 i_colors_7; -in vec4 i_colors_8; -in vec4 i_offsets_1; -in vec4 i_offsets_2; -in vec4 i_direction; -in vec4 i_position_and_size; -in vec4 i_border_color; -in vec4 i_border_radius; -in float i_border_width; - -out vec4 v_colors[8]; -out vec4 v_offsets[2]; -out vec4 v_direction; -out vec4 v_position_and_size; -out vec4 v_border_color; -out vec4 v_border_radius; -out float v_border_width; - -vec2 positions[4] = vec2[]( - vec2(0.0, 0.0), - vec2(0.0, 1.0), - vec2(1.0, 0.0), - vec2(1.0, 1.0) -); - -void main() { - vec2 i_position = i_position_and_size.xy; - vec2 i_size = i_position_and_size.zw; - - vec2 q_position = positions[gl_VertexID]; - vec2 p_position = i_position * u_scale; - vec2 p_size = i_size * u_scale; - - vec4 i_border_radius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_size.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - v_colors = vec4[]( - i_colors_1, - i_colors_2, - i_colors_3, - i_colors_4, - i_colors_5, - i_colors_6, - i_colors_7, - i_colors_8 - ); - - v_offsets = vec4[]( - i_offsets_1, - i_offsets_2 - ); - - v_direction = i_direction * u_scale; - v_position_and_size = vec4(p_position, p_size); - v_border_color = i_border_color; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(q_position, 0.0, 1.0); -} diff --git a/glow/src/shader/quad/core/solid.frag b/glow/src/shader/quad/core/solid.frag deleted file mode 100644 index ffb3b08ae6..0000000000 --- a/glow/src/shader/quad/core/solid.frag +++ /dev/null @@ -1,93 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -#ifdef HIGHER_THAN_300 -out vec4 fragColor; -#define gl_FragColor fragColor -#endif - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -in vec4 v_color; -in vec4 v_border_color; -in vec2 v_position; -in vec2 v_scale; -in vec4 v_border_radius; -in float v_border_width; - -void main() { - vec4 mixed_color; - - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_scale * 0.5).xy - ); - - if (v_border_width > 0.0) { - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_scale - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - mixed_color = mix(v_color, v_border_color, border_mix); - } else { - mixed_color = v_color; - } - - float d = fDistance( - fragCoord, - v_position, - v_scale, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/core/solid.vert b/glow/src/shader/quad/core/solid.vert deleted file mode 100644 index 2e5f1dd207..0000000000 --- a/glow/src/shader/quad/core/solid.vert +++ /dev/null @@ -1,52 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -in vec4 i_color; -in vec2 i_position; -in vec2 i_size; -in vec4 i_border_color; -in vec4 i_border_radius; -in float i_border_width; - -out vec4 v_color; -out vec4 v_border_color; -out vec2 v_position; -out vec2 v_scale; -out vec4 v_border_radius; -out float v_border_width; - -vec2 positions[4] = vec2[]( - vec2(0.0, 0.0), - vec2(0.0, 1.0), - vec2(1.0, 0.0), - vec2(1.0, 1.0) -); - -void main() { - vec2 q_position = positions[gl_VertexID]; - vec2 p_position = i_position * u_scale; - vec2 p_scale = i_size * u_scale; - - vec4 i_BorderRadius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.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), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - v_color = i_color; - v_border_color = i_border_color; - v_position = p_position; - v_scale = p_scale; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(q_position, 0.0, 1.0); -} diff --git a/glow/src/shader/triangle/gradient.frag b/glow/src/shader/triangle/gradient.frag deleted file mode 100644 index 435af3d0ee..0000000000 --- a/glow/src/shader/triangle/gradient.frag +++ /dev/null @@ -1,89 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -#ifdef HIGHER_THAN_300 -out vec4 fragColor; -#define gl_FragColor fragColor -#endif - -float random(vec2 coords) { - return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float noise_granularity = 0.3/255.0; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color += mix(-noise_granularity, noise_granularity, random(raw_position)); -} - -in vec2 v_raw_position; -in vec4 v_colors[8]; -in vec4 v_offsets[2]; -in vec4 v_direction; - -void main() { - gl_FragColor = gradient(v_direction, v_raw_position, v_offsets, v_colors); -} diff --git a/glow/src/shader/triangle/gradient.vert b/glow/src/shader/triangle/gradient.vert deleted file mode 100644 index ab8c59f330..0000000000 --- a/glow/src/shader/triangle/gradient.vert +++ /dev/null @@ -1,42 +0,0 @@ -uniform mat4 u_transform; - -in vec2 i_position; -in vec4 i_colors_1; -in vec4 i_colors_2; -in vec4 i_colors_3; -in vec4 i_colors_4; -in vec4 i_colors_5; -in vec4 i_colors_6; -in vec4 i_colors_7; -in vec4 i_colors_8; -in vec4 i_offsets_1; -in vec4 i_offsets_2; -in vec4 i_direction; - -out vec2 v_raw_position; -out vec4 v_colors[8]; -out vec4 v_offsets[2]; -out vec4 v_direction; - -void main() { - gl_Position = u_transform * vec4(i_position, 0.0, 1.0); - - v_raw_position = i_position; - - v_colors[0] = i_colors_1; - - // array initializers are not supported in GLSL 1.0 (ES 2.0) - v_colors[0] = i_colors_1; - v_colors[1] = i_colors_2; - v_colors[2] = i_colors_3; - v_colors[3] = i_colors_4; - v_colors[4] = i_colors_5; - v_colors[5] = i_colors_6; - v_colors[6] = i_colors_7; - v_colors[7] = i_colors_8; - - v_offsets[0] = i_offsets_1; - v_offsets[1] = i_offsets_2; - - v_direction = i_direction; -} diff --git a/glow/src/shader/triangle/solid.vert b/glow/src/shader/triangle/solid.vert deleted file mode 100644 index d0e6d780cb..0000000000 --- a/glow/src/shader/triangle/solid.vert +++ /dev/null @@ -1,11 +0,0 @@ -uniform mat4 u_transform; - -in vec2 i_position; -in vec4 i_color; - -out vec4 v_color; - -void main() { - gl_Position = u_transform * vec4(i_position, 0.0, 1.0); - v_color = i_color; -} diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 8db1594a72..afb4755bfa 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -16,8 +16,6 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use style::Style; pub use text::Text; -pub use iced_core::gradient::{self, Gradient}; - use crate::Primitive; #[derive(Debug, Clone)] diff --git a/graphics/src/geometry/fill.rs b/graphics/src/geometry/fill.rs index 2e8c1669be..025350cde1 100644 --- a/graphics/src/geometry/fill.rs +++ b/graphics/src/geometry/fill.rs @@ -1,5 +1,6 @@ //! Fill [crate::widget::canvas::Geometry] with a certain style. -use iced_core::{Color, Gradient}; +use crate::core::Color; +use crate::Gradient; pub use crate::geometry::Style; diff --git a/graphics/src/geometry/style.rs b/graphics/src/geometry/style.rs index be9ee376de..866bf13276 100644 --- a/graphics/src/geometry/style.rs +++ b/graphics/src/geometry/style.rs @@ -1,4 +1,5 @@ -use iced_core::{Color, Gradient}; +use crate::core::Color; +use crate::Gradient; /// The coloring style of some drawing. #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs new file mode 100644 index 0000000000..adfe414f2d --- /dev/null +++ b/graphics/src/gradient.rs @@ -0,0 +1,128 @@ +//! For creating a Gradient that can be used as a [`Fill`] for a mesh. +use crate::core::Point; +pub use linear::Linear; + +#[derive(Debug, Clone, PartialEq)] +/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), +/// or conically (TBD). +/// +/// For a gradient which can be used as a fill for a background of a widget, see [`core::Gradient`]. +pub enum Gradient { + /// A linear gradient interpolates colors along a direction from its `start` to its `end` + /// point. + Linear(Linear), +} + +impl Gradient { + /// Creates a new linear [`linear::Builder`]. + /// + /// The `start` and `end` [`Point`]s define the absolute position of the [`Gradient`]. + pub fn linear(start: Point, end: Point) -> linear::Builder { + linear::Builder::new(start, end) + } +} + +pub mod linear { + //! Linear gradient builder & definition. + use crate::Gradient; + use iced_core::gradient::linear::BuilderError; + use iced_core::gradient::ColorStop; + use iced_core::{Color, Point}; + + /// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. + /// + /// [`Fill`]: crate::widget::canvas::Fill + /// [`Stroke`]: crate::widget::canvas::Stroke + #[derive(Debug, Clone, Copy, PartialEq)] + pub struct Linear { + /// The absolute starting position of the gradient. + pub start: Point, + + /// The absolute ending position of the gradient. + pub end: Point, + + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: [ColorStop; 8], + } + + /// A [`Linear`] builder. + #[derive(Debug)] + pub struct Builder { + start: Point, + end: Point, + stops: [ColorStop; 8], + error: Option, + } + + impl Builder { + /// Creates a new [`Builder`]. + pub fn new(start: Point, end: Point) -> Self { + Self { + start, + end, + stops: std::array::from_fn(|_| ColorStop { + offset: 2.0, //default offset = invalid + color: Default::default(), + }), + error: None, + } + } + + /// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0` or the gradient cannot be built. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if offset.is_finite() && (0.0..=1.0).contains(&offset) { + match self.stops.binary_search_by(|stop| { + stop.offset.partial_cmp(&offset).unwrap() + }) { + Ok(_) => { + self.error = Some(BuilderError::DuplicateOffset(offset)) + } + Err(index) => { + if index < 8 { + self.stops[index] = ColorStop { offset, color }; + } + } + } + } else { + self.error = Some(BuilderError::InvalidOffset(offset)) + }; + + self + } + + /// Adds multiple [`ColorStop`]s to the gradient. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stops( + mut self, + stops: impl IntoIterator, + ) -> Self { + for stop in stops.into_iter() { + self = self.add_stop(stop.offset, stop.color) + } + + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `BuilderError` if gradient in invalid. + pub fn build(self) -> Result { + if self.stops.is_empty() { + Err(BuilderError::MissingColorStop) + } else if let Some(error) = self.error { + Err(error) + } else { + Ok(Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: self.stops, + })) + } + } + } +} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 0c50db52d5..a7d1541a19 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -23,6 +23,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod antialiasing; mod error; +mod gradient; mod transformation; mod viewport; @@ -41,6 +42,7 @@ pub use antialiasing::Antialiasing; pub use backend::Backend; pub use compositor::Compositor; pub use error::Error; +pub use gradient::Gradient; pub use primitive::Primitive; pub use renderer::Renderer; pub use transformation::Transformation; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 50f38d6b70..7050e5fc4c 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,7 +1,7 @@ use iced_core::alignment; use iced_core::image; use iced_core::svg; -use iced_core::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; +use iced_core::{Background, Color, Font, Rectangle, Size, Vector}; use bytemuck::{Pod, Zeroable}; use std::sync::Arc; @@ -158,25 +158,34 @@ pub struct Mesh2D { pub indices: Vec, } -/// A two-dimensional vertex. +/// A two-dimensional vertex with a color. #[derive(Copy, Clone, Debug, Zeroable, Pod)] #[repr(C)] -pub struct Vertex2D { +pub struct ColoredVertex2D { /// The vertex position in 2D space. pub position: [f32; 2], + + /// The color of the vertex in __linear__ RGBA. + pub color: [f32; 4], } -/// A two-dimensional vertex with a color. -#[derive(Copy, Clone, Debug, Zeroable, Pod)] +/// A vertex which contains 2D position & packed gradient data. +#[derive(Copy, Clone, Debug)] #[repr(C)] -pub struct ColoredVertex2D { +pub struct GradientVertex2D { /// The vertex position in 2D space. pub position: [f32; 2], - /// The color of the vertex in __linear__ RGBA. - pub color: [f32; 4], + /// The packed vertex data of the gradient. + pub gradient: [f32; 44], } +#[allow(unsafe_code)] +unsafe impl Zeroable for GradientVertex2D {} + +#[allow(unsafe_code)] +unsafe impl Pod for GradientVertex2D {} + impl From<()> for Primitive { fn from(_: ()) -> Self { Self::Group { primitives: vec![] } diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs deleted file mode 100644 index 09b6176753..0000000000 --- a/graphics/src/triangle.rs +++ /dev/null @@ -1 +0,0 @@ -//! Draw geometry using meshes of triangles. diff --git a/src/lib.rs b/src/lib.rs index c59d505804..e441e27b48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,8 +189,8 @@ pub use style::theme; pub use crate::core::alignment; pub use crate::core::event; pub use crate::core::{ - color, Alignment, Background, Color, ContentFit, Length, Padding, Point, - Rectangle, Size, Vector, + color, Alignment, Background, Color, ContentFit, Degrees, Gradient, Length, + Padding, Point, Radians, Rectangle, Size, Vector, }; pub use crate::runtime::Command; diff --git a/style/src/button.rs b/style/src/button.rs index 31570af052..32ec28b711 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -69,7 +69,7 @@ pub trait StyleSheet { ..color }), Background::Gradient(gradient) => { - Background::Gradient(gradient.transparentize(0.5)) + Background::Gradient(gradient.mul_alpha(0.5)) } }), text_color: Color { diff --git a/style/src/theme.rs b/style/src/theme.rs index 6993e415f1..dd7ca45a12 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -218,7 +218,7 @@ impl button::StyleSheet for Theme { ..color }), Background::Gradient(gradient) => { - Background::Gradient(gradient.transparentize(0.5)) + Background::Gradient(gradient.mul_alpha(0.5)) } }), text_color: Color { diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 271d026f15..7acab649af 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,5 +1,5 @@ -use crate::core::alignment; use crate::core::text; +use crate::core::{alignment, Gradient}; use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; use crate::graphics::backend; use crate::graphics::{Primitive, Viewport}; @@ -128,6 +128,9 @@ impl Backend { *color, )) } + Background::Gradient(gradient) => { + into_gradient(*gradient, *bounds) + } }, anti_alias: true, ..tiny_skia::Paint::default() @@ -318,6 +321,42 @@ fn into_color(color: Color) -> tiny_skia::Color { .expect("Convert color from iced to tiny_skia") } +fn into_gradient<'a>( + gradient: Gradient, + bounds: Rectangle, +) -> tiny_skia::Shader<'a> { + let Gradient::Linear(linear) = gradient; + let (start, end) = linear.angle.to_distance(&bounds); + + tiny_skia::LinearGradient::new( + tiny_skia::Point { + x: start.x, + y: start.y, + }, + tiny_skia::Point { x: end.x, y: end.y }, + linear + .color_stops + .into_iter() + .filter(|stop| stop.offset <= 1.0) + .map(|stop| { + tiny_skia::GradientStop::new( + stop.offset, + tiny_skia::Color::from_rgba( + stop.color.b, + stop.color.g, + stop.color.r, + stop.color.a, + ) + .expect("Create color"), + ) + }) + .collect(), + tiny_skia::SpreadMode::Pad, + tiny_skia::Transform::identity(), + ) + .expect("Create linear gradient") +} + fn rounded_rectangle( bounds: Rectangle, border_radius: [f32; 4], diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index c66621dd8c..bf03fae375 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,8 +1,8 @@ -use crate::core::Gradient; use crate::core::{Point, Rectangle, Size, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{Path, Style, Text}; +use crate::graphics::Gradient; use crate::graphics::Primitive; pub struct Frame { @@ -230,6 +230,7 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> { linear .color_stops .into_iter() + .filter(|stop| stop.offset <= 1.0) .map(|stop| { tiny_skia::GradientStop::new( stop.offset, diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 3aef4ff4de..f5e86dbb07 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -46,10 +46,6 @@ version = "0.2" git = "https://github.com/hecrj/glyphon.git" rev = "47050174841a4f58fc8d85c943a2117f72f19e8e" -[dependencies.encase] -version = "0.3.0" -features = ["glam"] - [dependencies.glam] version = "0.21.3" diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs index ecfca1025b..9412218729 100644 --- a/wgpu/src/buffer.rs +++ b/wgpu/src/buffer.rs @@ -6,7 +6,8 @@ pub struct Buffer { label: &'static str, size: u64, usage: wgpu::BufferUsages, - raw: wgpu::Buffer, + pub(crate) raw: wgpu::Buffer, + offsets: Vec, type_: PhantomData, } @@ -31,6 +32,7 @@ impl Buffer { size, usage, raw, + offsets: Vec::new(), type_: PhantomData, } } @@ -39,6 +41,8 @@ impl Buffer { let new_size = (std::mem::size_of::() * new_count) as u64; if self.size < new_size { + self.offsets.clear(); + self.raw = device.create_buffer(&wgpu::BufferDescriptor { label: Some(self.label), size: new_size, @@ -54,17 +58,19 @@ impl Buffer { } } + /// Returns the size of the written bytes. pub fn write( - &self, + &mut self, queue: &wgpu::Queue, - offset_count: usize, + offset: usize, contents: &[T], - ) { - queue.write_buffer( - &self.raw, - (std::mem::size_of::() * offset_count) as u64, - bytemuck::cast_slice(contents), - ); + ) -> usize { + let bytes: &[u8] = bytemuck::cast_slice(contents); + queue.write_buffer(&self.raw, offset as u64, bytes); + + self.offsets.push(offset as u64); + + bytes.len() } pub fn slice( @@ -73,263 +79,28 @@ impl Buffer { ) -> wgpu::BufferSlice<'_> { self.raw.slice(bounds) } -} - -fn next_copy_size(amount: usize) -> u64 { - let align_mask = wgpu::COPY_BUFFER_ALIGNMENT - 1; - - (((std::mem::size_of::() * amount).next_power_of_two() as u64 - + align_mask) - & !align_mask) - .max(wgpu::COPY_BUFFER_ALIGNMENT) -} - -//TODO(shan) -/// A generic buffer struct useful for items which have no alignment requirements -/// (e.g. Vertex, Index buffers) & no dynamic offsets. -#[derive(Debug)] -pub struct Static { - //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer - offsets: Vec, - label: &'static str, - usages: wgpu::BufferUsages, - gpu: wgpu::Buffer, - size: wgpu::BufferAddress, - _data: PhantomData, -} - -impl Static { - /// Initialize a new static buffer. - pub fn new( - device: &wgpu::Device, - label: &'static str, - usages: wgpu::BufferUsages, - count: usize, - ) -> Self { - let size = (mem::size_of::() * count) as u64; - - Self { - offsets: Vec::new(), - label, - usages, - gpu: Self::gpu_buffer(device, label, size, usages), - size, - _data: PhantomData, - } - } - - 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. - pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool { - let size = (mem::size_of::() * new_count) as u64; - - if self.size < size { - self.offsets.clear(); - self.size = size; - self.gpu = Self::gpu_buffer(device, self.label, size, self.usages); - true - } else { - false - } - } - - /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. - /// - /// Returns the size of the written bytes. - 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) { - let mut buffer = staging_belt.write_buffer( - encoder, - &self.gpu, - offset, - buffer_size, - device, - ); - - buffer.copy_from_slice(bytes); - - self.offsets.push(offset); - } - - bytes_size - } - - fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { - self.offsets - .get(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 - /// 1 that we stored earlier when writing. pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { - self.gpu.slice(self.offset_at(index)..) + self.raw.slice(self.offset_at(index)..) } - /// Clears any temporary data from the buffer. + /// Clears any temporary data (i.e. offsets) from the buffer. pub fn clear(&mut self) { self.offsets.clear() } - /// Returns a reference to the GPU buffer. - pub fn raw(&self) -> &wgpu::Buffer { - &self.gpu - } -} - -/// A dynamic uniform buffer is any type of buffer which does not have a static offset. -pub struct DynamicUniform { - offsets: Vec, - cpu: encase::DynamicUniformBuffer>, - gpu: wgpu::Buffer, - label: &'static str, - size: u64, - _data: PhantomData, -} - -impl Debug for DynamicUniform { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "{:?}, {:?}, {:?}, {:?}", - self.offsets, self.gpu, self.label, self.size - ) + /// Returns the offset at `index`, if it exists. + fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { + self.offsets.get(index).expect("No offset at index.") } } -impl DynamicUniform { - pub fn new(device: &wgpu::Device, label: &'static str) -> Self { - let initial_size = u64::from(T::min_size()); - - Self { - offsets: Vec::new(), - cpu: encase::DynamicUniformBuffer::new(Vec::new()), - gpu: DynamicUniform::::create_gpu_buffer( - device, - label, - wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - initial_size, - ), - label, - size: initial_size, - _data: Default::default(), - } - } - - fn create_gpu_buffer( - device: &wgpu::Device, - label: &'static str, - usage: wgpu::BufferUsages, - size: u64, - ) -> wgpu::Buffer { - device.create_buffer(&wgpu::BufferDescriptor { - label: Some(label), - size, - usage, - mapped_at_creation: false, - }) - } - - /// Write a new value to the CPU buffer with proper alignment. Stores the returned offset value - /// in the buffer for future use. - pub fn push(&mut self, value: &T) { - //this write operation on the cpu buffer will adjust for uniform alignment requirements - let offset = self - .cpu - .write(value) - .expect("Error when writing to dynamic uniform buffer.") - as wgpu::DynamicOffset; - - self.offsets.push(offset); - } - - /// Resize buffer contents if necessary. This will re-create the GPU buffer if current size is - /// less than the newly computed size from the CPU buffer. - /// - /// If the gpu buffer is resized, its bind group will need to be recreated! - pub fn resize(&mut self, device: &wgpu::Device) -> bool { - let new_size = self.cpu.as_ref().len() as u64; - - if self.size < new_size { - self.gpu = DynamicUniform::::create_gpu_buffer( - device, - self.label, - wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - new_size, - ); - self.size = new_size; - true - } else { - false - } - } - - /// Write the contents of this dynamic uniform buffer to the GPU via staging belt command. - pub fn write( - &mut self, - device: &wgpu::Device, - staging_belt: &mut wgpu::util::StagingBelt, - encoder: &mut wgpu::CommandEncoder, - ) { - let size = self.cpu.as_ref().len(); - - if let Some(buffer_size) = wgpu::BufferSize::new(size as u64) { - let mut buffer = staging_belt.write_buffer( - encoder, - &self.gpu, - 0, - buffer_size, - device, - ); - - buffer.copy_from_slice(self.cpu.as_ref()); - } - } - - // Gets the aligned offset at the given index from the CPU buffer. - pub fn offset_at_index(&self, index: usize) -> wgpu::DynamicOffset { - let offset = self - .offsets - .get(index) - .copied() - .expect("Index not found in offsets."); - - offset - } - - /// Returns a reference to the GPU buffer. - pub fn raw(&self) -> &wgpu::Buffer { - &self.gpu - } +fn next_copy_size(amount: usize) -> u64 { + let align_mask = wgpu::COPY_BUFFER_ALIGNMENT - 1; - /// Reset the buffer. - pub fn clear(&mut self) { - self.offsets.clear(); - self.cpu.as_mut().clear(); - self.cpu.set_offset(0); - } + (((std::mem::size_of::() * amount).next_power_of_two() as u64 + + align_mask) + & !align_mask) + .max(wgpu::COPY_BUFFER_ALIGNMENT) } diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/wgpu/src/buffer/static.rs b/wgpu/src/buffer/static.rs deleted file mode 100644 index d8ae116e4c..0000000000 --- a/wgpu/src/buffer/static.rs +++ /dev/null @@ -1,107 +0,0 @@ -use bytemuck::{Pod, Zeroable}; -use std::marker::PhantomData; -use std::mem; - -const DEFAULT_COUNT: wgpu::BufferAddress = 128; - -/// A generic buffer struct useful for items which have no alignment requirements -/// (e.g. Vertex, Index buffers) & no dynamic offsets. -#[derive(Debug)] -pub struct Buffer { - //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer - offsets: Vec, - label: &'static str, - usages: wgpu::BufferUsages, - gpu: wgpu::Buffer, - size: wgpu::BufferAddress, - _data: PhantomData, -} - -impl Buffer { - /// Initialize a new static buffer. - pub fn new( - device: &wgpu::Device, - label: &'static str, - usages: wgpu::BufferUsages, - ) -> Self { - let size = (mem::size_of::() as u64) * DEFAULT_COUNT; - - Self { - offsets: Vec::new(), - label, - usages, - gpu: Self::gpu_buffer(device, label, size, usages), - size, - _data: PhantomData, - } - } - - 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 mesh data - /// changes & a redraw is requested. - pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool { - let size = (mem::size_of::() * new_count) as u64; - - if self.size < size { - self.size = - (mem::size_of::() * (new_count + new_count / 2)) as u64; - - self.gpu = - Self::gpu_buffer(device, self.label, self.size, self.usages); - - self.offsets.clear(); - true - } else { - false - } - } - - /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. - /// - /// Returns the size of the written bytes. - pub fn write( - &mut self, - queue: &wgpu::Queue, - offset: u64, - content: &[T], - ) -> u64 { - let bytes = bytemuck::cast_slice(content); - let bytes_size = bytes.len() as u64; - - queue.write_buffer(&self.gpu, offset, bytes); - self.offsets.push(offset); - - bytes_size - } - - fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { - self.offsets - .get(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 - /// 1 that we stored earlier when writing. - pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { - self.gpu.slice(self.offset_at(index)..) - } - - /// Clears any temporary data from the buffer. - pub fn clear(&mut self) { - self.offsets.clear() - } -} diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index ab795ab8b6..44b1a89f57 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,9 +1,10 @@ -use crate::core::{Gradient, Point, Rectangle, Size, Vector}; +use crate::core::{Point, Rectangle, Size, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, }; use crate::graphics::primitive::{self, Primitive}; +use crate::graphics::Gradient; use lyon::geom::euclid; use lyon::tessellation; @@ -24,10 +25,7 @@ pub struct Frame { enum Buffer { Solid(tessellation::VertexBuffers), - Gradient( - tessellation::VertexBuffers, - Gradient, - ), + Gradient(tessellation::VertexBuffers), } struct BufferStack { @@ -501,7 +499,7 @@ impl tessellation::FillVertexConstructor primitive::GradientVertex2D { position: [position.x, position.y], - gradient: self.gradient.pack(), + gradient: pack_gradient(&self.gradient), } } } @@ -515,9 +513,9 @@ impl tessellation::StrokeVertexConstructor ) -> primitive::GradientVertex2D { let position = vertex.position(); - triangle::GradientVertex2D { + primitive::GradientVertex2D { position: [position.x, position.y], - gradient: self.gradient.pack(), + gradient: pack_gradient(&self.gradient), } } } @@ -620,3 +618,40 @@ pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { ); }) } + +/// Packs the [`Gradient`] for use in shader code. +fn pack_gradient(gradient: &Gradient) -> [f32; 44] { + match gradient { + Gradient::Linear(linear) => { + let mut pack: [f32; 44] = [0.0; 44]; + let mut offsets: [f32; 8] = [2.0; 8]; + + for (index, stop) in linear.color_stops.iter().enumerate().take(8) { + let [r, g, b, a] = stop.color.into_linear(); + + pack[(index * 4)] = r; + pack[(index * 4) + 1] = g; + pack[(index * 4) + 2] = b; + pack[(index * 4) + 3] = a; + + offsets[index] = stop.offset; + } + + pack[32] = offsets[0]; + pack[33] = offsets[1]; + pack[34] = offsets[2]; + pack[35] = offsets[3]; + pack[36] = offsets[4]; + pack[37] = offsets[5]; + pack[38] = offsets[6]; + pack[39] = offsets[7]; + + pack[40] = linear.start.x; + pack[41] = linear.start.y; + pack[42] = linear.end.x; + pack[43] = linear.end.y; + + pack + } + } +} diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 4163e3382f..b8d890911b 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -8,10 +8,10 @@ mod vector; use atlas::Atlas; +use crate::buffer::Buffer; use crate::core::{Rectangle, Size}; use crate::graphics::Transformation; -use crate::layer; -use crate::Buffer; +use crate::{layer, quad}; use std::cell::RefCell; use std::mem; @@ -121,7 +121,7 @@ impl Layer { ); let _ = self.instances.resize(device, instances.len()); - self.instances.write(queue, 0, instances); + let _ = self.instances.write(queue, 0, instances); self.instance_count = instances.len(); } @@ -131,7 +131,7 @@ impl Layer { render_pass.set_vertex_buffer(1, self.instances.slice(..)); render_pass.draw_indexed( - 0..QUAD_INDICES.len() as u32, + 0..quad::INDICES.len() as u32, 0, 0..self.instance_count as u32, ); @@ -244,22 +244,7 @@ impl Pipeline { fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], + targets: &quad::color_target_state(format), }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -278,14 +263,14 @@ impl Pipeline { let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::image vertex buffer"), - contents: bytemuck::cast_slice(&QUAD_VERTS), + contents: bytemuck::cast_slice(&quad::VERTICES), usage: wgpu::BufferUsages::VERTEX, }); let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::image index buffer"), - contents: bytemuck::cast_slice(&QUAD_INDICES), + contents: bytemuck::cast_slice(&quad::INDICES), usage: wgpu::BufferUsages::INDEX, }); @@ -498,23 +483,6 @@ pub struct Vertex { _position: [f32; 2], } -const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; - -const QUAD_VERTS: [Vertex; 4] = [ - Vertex { - _position: [0.0, 0.0], - }, - Vertex { - _position: [1.0, 0.0], - }, - Vertex { - _position: [1.0, 1.0], - }, - Vertex { - _position: [0.0, 1.0], - }, -]; - #[repr(C)] #[derive(Debug, Clone, Copy, Zeroable, Pod)] struct Instance { diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 29e59ed045..f982818e80 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -7,6 +7,7 @@ pub mod quad; pub use image::Image; pub use mesh::Mesh; +pub use quad::{Gradient, Solid}; pub use text::Text; use crate::core::alignment; @@ -154,8 +155,7 @@ impl<'a> Layer<'a> { } => { let layer = &mut layers[current_layer]; - // TODO: Move some of these computations to the GPU (?) - let properties = Properties { + let properties = quad::Properties { position: [ bounds.x + translation.x, bounds.y + translation.y, @@ -175,10 +175,13 @@ impl<'a> Layer<'a> { } Background::Gradient(gradient) => { let quad = quad::Gradient { - gradient: (*gradient).pack(Rectangle::new( - properties.position.into(), - properties.size.into(), - )), + gradient: pack_gradient( + gradient, + Rectangle::new( + properties.position.into(), + properties.size.into(), + ), + ), properties, }; @@ -296,3 +299,34 @@ impl<'a> Layer<'a> { } } } + +/// Packs the [`Gradient`] for use in shader code. +fn pack_gradient( + gradient: &crate::core::Gradient, + bounds: Rectangle, +) -> [f32; 44] { + match gradient { + crate::core::Gradient::Linear(linear) => { + let mut pack: [f32; 44] = [0.0; 44]; + + for (index, stop) in linear.color_stops.iter().enumerate().take(8) { + let [r, g, b, a] = stop.color.into_linear(); + + pack[(index * 4)] = r; + pack[(index * 4) + 1] = g; + pack[(index * 4) + 2] = b; + pack[(index * 4) + 3] = a; + pack[32 + index] = stop.offset; + } + + let (start, end) = linear.angle.to_distance(&bounds); + + pack[40] = start.x; + pack[41] = start.y; + pack[42] = end.x; + pack[43] = end.y; + + pack + } + } +} diff --git a/wgpu/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 20e44451fa..b7dd9a0ba4 100644 --- a/wgpu/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,5 +1,5 @@ //! A collection of triangle primitives. -use crate::core::{Gradient, Point, Rectangle}; +use crate::core::{Point, Rectangle}; use crate::graphics::primitive; /// A mesh of triangles. @@ -62,9 +62,15 @@ pub struct AttributeCount { /// The total amount of solid vertices. pub solid_vertices: usize, + /// The total amount of solid meshes. + pub solids: usize, + /// The total amount of gradient vertices. pub gradient_vertices: usize, + /// The total amount of gradient meshes. + pub gradients: usize, + /// The total amount of indices. pub indices: usize, } @@ -76,10 +82,12 @@ pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> AttributeCount { .fold(AttributeCount::default(), |mut count, mesh| { match mesh { Mesh::Solid { buffers, .. } => { + count.solids += 1; count.solid_vertices += buffers.vertices.len(); count.indices += buffers.indices.len(); } Mesh::Gradient { buffers, .. } => { + count.gradients += 1; count.gradient_vertices += buffers.vertices.len(); count.indices += buffers.indices.len(); } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 473f362177..22fbb2b2b5 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -59,8 +59,6 @@ pub use backend::Backend; pub use layer::Layer; pub use settings::Settings; -use buffer::Buffer; - #[cfg(any(feature = "image", feature = "svg"))] mod image; diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 04301bd60e..c6054d732b 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -1,16 +1,19 @@ use crate::core::Rectangle; use crate::graphics::Transformation; use crate::layer; -use crate::Buffer; +use std::mem; use wgpu::util::DeviceExt; #[cfg(feature = "tracing")] use tracing::info_span; +const INITIAL_INSTANCES: usize = 10_000; + #[derive(Debug)] pub struct Pipeline { - pipeline: wgpu::RenderPipeline, + solid: solid::Pipeline, + gradient: gradient::Pipeline, constant_layout: wgpu::BindGroupLayout, vertices: wgpu::Buffer, indices: wgpu::Buffer, @@ -37,107 +40,28 @@ impl Pipeline { }], }); - let layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("iced_wgpu::quad pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&constant_layout], - }); - - let shader = - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("iced_wgpu::quad::shader"), - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( - include_str!("shader/quad.wgsl"), - )), - }); - - let pipeline = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("iced_wgpu::quad pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[ - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - shader_location: 0, - format: wgpu::VertexFormat::Float32x2, - offset: 0, - }], - }, - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &wgpu::vertex_attr_array!( - 1 => Float32x2, - 2 => Float32x2, - 3 => Float32x4, - 4 => Float32x4, - 5 => Float32x4, - 6 => Float32, - ), - }, - ], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - front_face: wgpu::FrontFace::Cw, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }); - let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::quad vertex buffer"), - contents: bytemuck::cast_slice(&QUAD_VERTS), + contents: bytemuck::cast_slice(&VERTICES), usage: wgpu::BufferUsages::VERTEX, }); let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::quad index buffer"), - contents: bytemuck::cast_slice(&QUAD_INDICES), + contents: bytemuck::cast_slice(&INDICES), usage: wgpu::BufferUsages::INDEX, }); - Pipeline { - pipeline, - constant_layout, + Self { vertices, indices, + solid: solid::Pipeline::new(device, format, &constant_layout), + gradient: gradient::Pipeline::new(device, format, &constant_layout), layers: Vec::new(), prepare_layer: 0, + constant_layout, } } @@ -145,7 +69,7 @@ impl Pipeline { &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - instances: &[layer::Quad], + instances: &layer::Quads, transformation: Transformation, scale: f32, ) { @@ -166,22 +90,27 @@ impl Pipeline { render_pass: &mut wgpu::RenderPass<'a>, ) { if let Some(layer) = self.layers.get(layer) { - render_pass.set_pipeline(&self.pipeline); - render_pass.set_scissor_rect( bounds.x, bounds.y, bounds.width, bounds.height, ); - render_pass.set_index_buffer( self.indices.slice(..), wgpu::IndexFormat::Uint16, ); render_pass.set_vertex_buffer(0, self.vertices.slice(..)); - layer.draw(render_pass); + if layer.solid.instance_count > 0 { + render_pass.set_pipeline(&self.solid.pipeline); + layer.solid.draw(&layer.constants, render_pass); + } + + if layer.gradient.instance_count > 0 { + render_pass.set_pipeline(&self.gradient.pipeline); + layer.gradient.draw(&layer.constants, render_pass); + } } } @@ -194,8 +123,8 @@ impl Pipeline { struct Layer { constants: wgpu::BindGroup, constants_buffer: wgpu::Buffer, - instances: Buffer, - instance_count: usize, + solid: solid::Layer, + gradient: gradient::Layer, } impl Layer { @@ -219,18 +148,11 @@ impl Layer { }], }); - let instances = Buffer::new( - device, - "iced_wgpu::quad instance buffer", - INITIAL_INSTANCES, - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - ); - Self { constants, constants_buffer, - instances, - instance_count: 0, + solid: solid::Layer::new(device), + gradient: gradient::Layer::new(device), } } @@ -238,7 +160,7 @@ impl Layer { &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - instances: &[layer::Quad], + instances: &layer::Quads, transformation: Transformation, scale: f32, ) { @@ -253,35 +175,351 @@ impl Layer { bytemuck::bytes_of(&uniforms), ); - let _ = self.instances.resize(device, instances.len()); - self.instances.write(queue, 0, instances); - self.instance_count = instances.len(); + let _ = self.solid.instances.resize(device, instances.solids.len()); + let _ = self + .gradient + .instances + .resize(device, instances.gradients.len()); + let _ = + self.solid + .instances + .write(queue, 0, instances.solids.as_slice()); + self.solid.instance_count = instances.solids.len(); + let _ = self.gradient.instances.write( + queue, + 0, + instances.gradients.as_slice(), + ); + self.gradient.instance_count = instances.gradients.len(); } +} - pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - #[cfg(feature = "tracing")] - let _ = info_span!("Wgpu::Quad", "DRAW").entered(); +mod solid { + use crate::buffer::Buffer; + use crate::layer::quad; + use crate::quad::{color_target_state, Vertex, INDICES, INITIAL_INSTANCES}; + use std::mem; + #[cfg(feature = "tracing")] + use tracing::info_span; + + #[derive(Debug)] + pub struct Pipeline { + pub pipeline: wgpu::RenderPipeline, + } - render_pass.set_bind_group(0, &self.constants, &[]); - render_pass.set_vertex_buffer(1, self.instances.slice(..)); + #[derive(Debug)] + pub struct Layer { + pub instances: Buffer, + pub instance_count: usize, + } - render_pass.draw_indexed( - 0..QUAD_INDICES.len() as u32, - 0, - 0..self.instance_count as u32, - ); + impl Layer { + pub fn new(device: &wgpu::Device) -> Self { + let instances = Buffer::new( + device, + "iced_wgpu::quad::solid instance buffer", + INITIAL_INSTANCES, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + Self { + instances, + instance_count: 0, + } + } + + pub fn draw<'a>( + &'a self, + constants: &'a wgpu::BindGroup, + render_pass: &mut wgpu::RenderPass<'a>, + ) { + #[cfg(feature = "tracing")] + let _ = info_span!("Wgpu::Quad::Solid", "DRAW").entered(); + + render_pass.set_bind_group(0, constants, &[]); + render_pass.set_vertex_buffer(1, self.instances.slice(..)); + + render_pass.draw_indexed( + 0..INDICES.len() as u32, + 0, + 0..self.instance_count as u32, + ); + } + } + + impl Pipeline { + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + constants_layout: &wgpu::BindGroupLayout, + ) -> Self { + let layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("iced_wgpu::quad::solid pipeline layout"), + push_constant_ranges: &[], + bind_group_layouts: &[constants_layout], + }, + ); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("iced_wgpu::quad::solid shader"), + source: wgpu::ShaderSource::Wgsl( + std::borrow::Cow::Borrowed(include_str!( + "shader/quad.wgsl" + )), + ), + }); + + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::quad::solid pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "solid_vs_main", + buffers: &[ + Vertex::buffer_layout(), + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() + as u64, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &wgpu::vertex_attr_array!( + // Color + 1 => Float32x4, + // Position + 2 => Float32x2, + // Size + 3 => Float32x2, + // Border color + 4 => Float32x4, + // Border radius + 5 => Float32x4, + // Border width + 6 => Float32, + ), + }, + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "solid_fs_main", + targets: &color_target_state(format), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }, + ); + + Self { pipeline } + } + } +} + +mod gradient { + use crate::buffer::Buffer; + use crate::layer::quad; + use crate::quad::{color_target_state, Vertex, INDICES, INITIAL_INSTANCES}; + use std::mem; + #[cfg(feature = "tracing")] + use tracing::info_span; + + #[derive(Debug)] + pub struct Pipeline { + pub pipeline: wgpu::RenderPipeline, + } + + #[derive(Debug)] + pub struct Layer { + pub instances: Buffer, + pub instance_count: usize, + } + + impl Layer { + pub fn new(device: &wgpu::Device) -> Self { + let instances = Buffer::new( + device, + "iced_wgpu::quad::gradient instances buffer", + INITIAL_INSTANCES, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + Self { + instances, + instance_count: 0, + } + } + + pub fn draw<'a>( + &'a self, + constants: &'a wgpu::BindGroup, + render_pass: &mut wgpu::RenderPass<'a>, + ) { + #[cfg(feature = "tracing")] + let _ = info_span!("Wgpu::Quad::Gradient", "DRAW").entered(); + + render_pass.set_bind_group(0, constants, &[]); + render_pass.set_vertex_buffer(1, self.instances.slice(..)); + + render_pass.draw_indexed( + 0..INDICES.len() as u32, + 0, + 0..self.instance_count as u32, + ); + } + } + + impl Pipeline { + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + constants_layout: &wgpu::BindGroupLayout, + ) -> Self { + let layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("iced_wgpu::quad::gradient pipeline layout"), + push_constant_ranges: &[], + bind_group_layouts: &[constants_layout], + }, + ); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("iced_wgpu::quad::gradient shader"), + source: wgpu::ShaderSource::Wgsl( + std::borrow::Cow::Borrowed(include_str!( + "shader/quad.wgsl" + )), + ), + }); + + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::quad::gradient pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "gradient_vs_main", + buffers: &[ + Vertex::buffer_layout(), + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() + as u64, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &wgpu::vertex_attr_array!( + // Color 1 + 1 => Float32x4, + // Color 2 + 2 => Float32x4, + // Color 3 + 3 => Float32x4, + // Color 4 + 4 => Float32x4, + // Color 5 + 5 => Float32x4, + // Color 6 + 6 => Float32x4, + // Color 7 + 7 => Float32x4, + // Color 8 + 8 => Float32x4, + // Offsets 1-4 + 9 => Float32x4, + // Offsets 5-8 + 10 => Float32x4, + // Direction + 11 => Float32x4, + // Position & Scale + 12 => Float32x4, + // Border color + 13 => Float32x4, + // Border radius + 14 => Float32x4, + // Border width + 15 => Float32 + ), + }, + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "gradient_fs_main", + targets: &color_target_state(format), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }, + ); + + Self { pipeline } + } } } +pub(crate) fn color_target_state( + format: wgpu::TextureFormat, +) -> [Option; 1] { + [Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + }), + write_mask: wgpu::ColorWrites::ALL, + })] +} + #[repr(C)] -#[derive(Clone, Copy, Zeroable, Pod)] +#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)] pub struct Vertex { _position: [f32; 2], } -const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; +impl Vertex { + fn buffer_layout<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[wgpu::VertexAttribute { + shader_location: 0, + format: wgpu::VertexFormat::Float32x2, + offset: 0, + }], + } + } +} + +pub(crate) const INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; -const QUAD_VERTS: [Vertex; 4] = [ +pub(crate) const VERTICES: [Vertex; 4] = [ Vertex { _position: [0.0, 0.0], }, @@ -296,10 +534,8 @@ const QUAD_VERTS: [Vertex; 4] = [ }, ]; -const INITIAL_INSTANCES: usize = 10_000; - #[repr(C)] -#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)] struct Uniforms { transform: [f32; 16], scale: f32, @@ -327,275 +563,3 @@ impl Default for Uniforms { } } } - -// mod solid { -// use crate::buffer; -// use crate::quad::{QuadVertex, INITIAL_INSTANCES}; -// use iced_graphics::layer::quad; -// use std::mem; -// -// #[derive(Debug)] -// pub struct Pipeline { -// pub pipeline: wgpu::RenderPipeline, -// pub instances: buffer::Static, -// } -// -// impl Pipeline { -// pub fn new( -// device: &wgpu::Device, -// format: wgpu::TextureFormat, -// uniforms_layout: &wgpu::BindGroupLayout, -// ) -> Self { -// let instances = buffer::Static::new( -// device, -// "iced_wgpu::quad::solid instance buffer", -// wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, -// INITIAL_INSTANCES, -// ); -// -// let layout = device.create_pipeline_layout( -// &wgpu::PipelineLayoutDescriptor { -// label: Some("iced_wgpu::quad::solid pipeline layout"), -// push_constant_ranges: &[], -// bind_group_layouts: &[uniforms_layout], -// }, -// ); -// -// let shader = -// device.create_shader_module(wgpu::ShaderModuleDescriptor { -// label: Some("iced_wgpu::quad::solid shader"), -// source: wgpu::ShaderSource::Wgsl( -// std::borrow::Cow::Borrowed(include_str!( -// "shader/quad.wgsl" -// )), -// ), -// }); -// -// let pipeline = device.create_render_pipeline( -// &wgpu::RenderPipelineDescriptor { -// label: Some("iced_wgpu::quad::solid pipeline"), -// layout: Some(&layout), -// vertex: wgpu::VertexState { -// module: &shader, -// entry_point: "solid_vs_main", -// buffers: &[ -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Vertex, -// attributes: &[wgpu::VertexAttribute { -// shader_location: 0, -// format: wgpu::VertexFormat::Float32x2, -// offset: 0, -// }], -// }, -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Instance, -// attributes: &wgpu::vertex_attr_array!( -// // Color -// 1 => Float32x4, -// // Position -// 2 => Float32x2, -// // Size -// 3 => Float32x2, -// // Border color -// 4 => Float32x4, -// // Border radius -// 5 => Float32x4, -// // Border width -// 6 => Float32, -// ), -// }, -// ], -// }, -// fragment: Some(wgpu::FragmentState { -// module: &shader, -// entry_point: "solid_fs_main", -// targets: &[Some(wgpu::ColorTargetState { -// format, -// blend: Some(wgpu::BlendState { -// color: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::SrcAlpha, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// alpha: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::One, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// }), -// write_mask: wgpu::ColorWrites::ALL, -// })], -// }), -// primitive: wgpu::PrimitiveState { -// topology: wgpu::PrimitiveTopology::TriangleList, -// front_face: wgpu::FrontFace::Cw, -// ..Default::default() -// }, -// depth_stencil: None, -// multisample: wgpu::MultisampleState { -// count: 1, -// mask: !0, -// alpha_to_coverage_enabled: false, -// }, -// multiview: None, -// }, -// ); -// -// Self { -// pipeline, -// instances, -// } -// } -// } -// } -// -// mod gradient { -// use crate::buffer; -// use crate::quad::{QuadVertex, INITIAL_INSTANCES}; -// use iced_graphics::layer::quad; -// use std::mem; -// -// #[derive(Debug)] -// pub struct Pipeline { -// pub pipeline: wgpu::RenderPipeline, -// pub instances: buffer::Static, -// } -// -// impl Pipeline { -// pub fn new( -// device: &wgpu::Device, -// format: wgpu::TextureFormat, -// uniforms_layout: &wgpu::BindGroupLayout, -// ) -> Self { -// let instances = buffer::Static::new( -// device, -// "iced_wgpu::quad::gradient instance buffer", -// wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, -// INITIAL_INSTANCES, -// ); -// -// let layout = device.create_pipeline_layout( -// &wgpu::PipelineLayoutDescriptor { -// label: Some("iced_wgpu::quad::gradient pipeline layout"), -// push_constant_ranges: &[], -// bind_group_layouts: &[uniforms_layout], -// }, -// ); -// -// let shader = -// device.create_shader_module(wgpu::ShaderModuleDescriptor { -// label: Some("iced_wgpu::quad::gradient shader"), -// source: wgpu::ShaderSource::Wgsl( -// std::borrow::Cow::Borrowed(include_str!( -// "shader/quad.wgsl" -// )), -// ), -// }); -// -// let pipeline = device.create_render_pipeline( -// &wgpu::RenderPipelineDescriptor { -// label: Some("iced_wgpu::quad::gradient pipeline"), -// layout: Some(&layout), -// vertex: wgpu::VertexState { -// module: &shader, -// entry_point: "gradient_vs_main", -// buffers: &[ -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Vertex, -// attributes: &[wgpu::VertexAttribute { -// shader_location: 0, -// format: wgpu::VertexFormat::Float32x2, -// offset: 0, -// }], -// }, -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Instance, -// attributes: &wgpu::vertex_attr_array!( -// // Color 1 -// 1 => Float32x4, -// // Color 2 -// 2 => Float32x4, -// // Color 3 -// 3 => Float32x4, -// // Color 4 -// 4 => Float32x4, -// // Color 5 -// 5 => Float32x4, -// // Color 6 -// 6 => Float32x4, -// // Color 7 -// 7 => Float32x4, -// // Color 8 -// 8 => Float32x4, -// // Offsets 1-4 -// 9 => Float32x4, -// // Offsets 5-8 -// 10 => Float32x4, -// // Direction -// 11 => Float32x4, -// // Position & Scale -// 12 => Float32x4, -// // Border color -// 13 => Float32x4, -// // Border radius -// 14 => Float32x4, -// // Border width -// 15 => Float32 -// ), -// }, -// ], -// }, -// fragment: Some(wgpu::FragmentState { -// module: &shader, -// entry_point: "gradient_fs_main", -// targets: &[Some(wgpu::ColorTargetState { -// format, -// blend: Some(wgpu::BlendState { -// color: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::SrcAlpha, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// alpha: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::One, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// }), -// write_mask: wgpu::ColorWrites::ALL, -// })], -// }), -// primitive: wgpu::PrimitiveState { -// topology: wgpu::PrimitiveTopology::TriangleList, -// front_face: wgpu::FrontFace::Cw, -// ..Default::default() -// }, -// depth_stencil: None, -// multisample: wgpu::MultisampleState { -// count: 1, -// mask: !0, -// alpha_to_coverage_enabled: false, -// }, -// multiview: None, -// }, -// ); -// -// Self { -// pipeline, -// instances, -// } -// } -// } -// } \ No newline at end of file diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 95da0c2db5..0ba3b5b002 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,14 +1,18 @@ //! Draw meshes of triangles. mod msaa; -use crate::buffer::r#static::Buffer; -use crate::core::{Gradient, Size}; +use crate::buffer::Buffer; +use crate::core::Size; use crate::graphics::{Antialiasing, Transformation}; use crate::layer::mesh::{self, Mesh}; +use std::mem; #[cfg(feature = "tracing")] use tracing::info_span; +const INITIAL_INDEX_COUNT: usize = 1_000; +const INITIAL_VERTEX_COUNT: usize = 1_000; + #[derive(Debug)] pub struct Pipeline { blit: Option, @@ -36,6 +40,7 @@ impl Layer { index_buffer: Buffer::new( device, "iced_wgpu::triangle index buffer", + INITIAL_INDEX_COUNT, wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, ), index_strides: Vec::new(), @@ -49,7 +54,7 @@ impl Layer { device: &wgpu::Device, queue: &wgpu::Queue, solid: &solid::Pipeline, - #[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline, + gradient: &gradient::Pipeline, meshes: &[Mesh<'_>], transformation: Transformation, ) { @@ -62,92 +67,81 @@ impl Layer { // the majority of use cases. Therefore we will write GPU data every frame (for now). let _ = self.index_buffer.resize(device, count.indices); let _ = self.solid.vertices.resize(device, count.solid_vertices); - let _ = self.gradient.vertices.resize(device, count.gradient_vertices); + let _ = self + .gradient + .vertices + .resize(device, count.gradient_vertices); + + if self.solid.uniforms.resize(device, count.solids) { + self.solid.constants = solid::Layer::bind_group( + device, + &self.solid.uniforms.raw, + &solid.constants_layout, + ); + } + + if self.gradient.uniforms.resize(device, count.gradients) { + self.gradient.constants = gradient::Layer::bind_group( + device, + &self.gradient.uniforms.raw, + &gradient.constants_layout, + ); + } - // Prepare dynamic buffers & data store for writing - self.index_buffer.clear(); self.index_strides.clear(); + self.index_buffer.clear(); self.solid.vertices.clear(); - self.gradient.vertices.clear(); self.solid.uniforms.clear(); + self.gradient.vertices.clear(); self.gradient.uniforms.clear(); let mut solid_vertex_offset = 0; + let mut solid_uniform_offset = 0; let mut gradient_vertex_offset = 0; + let mut gradient_uniform_offset = 0; let mut index_offset = 0; for mesh in meshes { let origin = mesh.origin(); let indices = mesh.indices(); - let transform = - transformation * Transformation::translate(origin.x, origin.y); + let uniforms = Uniforms::new( + transformation * Transformation::translate(origin.x, origin.y), + ); - let new_index_offset = + index_offset += self.index_buffer.write(queue, index_offset, indices); - - index_offset += new_index_offset; self.index_strides.push(indices.len() as u32); - //push uniform data to CPU buffers match mesh { Mesh::Solid { buffers, .. } => { - self.solid.uniforms.push(&solid::Uniforms::new(transform)); - - let written_bytes = self.solid.vertices.write( + solid_vertex_offset += self.solid.vertices.write( queue, solid_vertex_offset, &buffers.vertices, ); - solid_vertex_offset += written_bytes; + solid_uniform_offset += self.solid.uniforms.write( + queue, + solid_uniform_offset, + &[uniforms], + ); } Mesh::Gradient { buffers, .. } => { - self.gradient.uniforms.push(&Uniforms { - transform: transform.into(), - }); - - let written_bytes = self.gradient.vertices.write( + gradient_vertex_offset += self.gradient.vertices.write( queue, gradient_vertex_offset, &buffers.vertices, ); - gradient_vertex_offset += written_bytes; + gradient_uniform_offset += self.gradient.uniforms.write( + queue, + gradient_uniform_offset, + &[uniforms], + ); } } } - - // Write uniform data to GPU - if count.solid_vertices > 0 { - let uniforms_resized = self.solid.uniforms.resize(device); - - if uniforms_resized { - self.solid.constants = solid::Layer::bind_group( - device, - self.solid.uniforms.raw(), - &solid.constants_layout, - ) - } - - self.solid.uniforms.write(queue); - } - - if count.gradient_vertices > 0 { - // Resize buffers if needed - let uniforms_resized = self.gradient.uniforms.resize(device); - - if uniforms_resized { - self.gradient.constants = gradient::Layer::bind_group( - device, - self.gradient.uniforms.raw(), - &self.gradient.bind_group_layout, - ); - } - - // Write to GPU - self.gradient.uniforms.write(queue); - } } fn render<'a>( @@ -183,7 +177,7 @@ impl Layer { render_pass.set_bind_group( 0, &self.solid.constants, - &[self.solid.uniforms.offset_at_index(num_solids)], + &[(num_solids * mem::size_of::()) as u32], ); render_pass.set_vertex_buffer( @@ -203,10 +197,7 @@ impl Layer { render_pass.set_bind_group( 0, &self.gradient.constants, - &[self - .gradient - .uniforms - .offset_at_index(num_gradients)], + &[(num_gradients * mem::size_of::()) as u32], ); render_pass.set_vertex_buffer( @@ -214,9 +205,9 @@ impl Layer { self.gradient.vertices.slice_from_index(num_gradients), ); - num_gradients += 1; - } - }; + num_gradients += 1; + } + }; render_pass.set_index_buffer( self.index_buffer.slice_from_index(index), @@ -237,10 +228,7 @@ impl Pipeline { Pipeline { blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), solid: solid::Pipeline::new(device, format, antialiasing), - - #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Pipeline::new(device, format, antialiasing), - layers: Vec::new(), prepare_layer: 0, } @@ -257,12 +245,8 @@ impl Pipeline { let _ = info_span!("Wgpu::Triangle", "PREPARE").entered(); if self.layers.len() <= self.prepare_layer { - self.layers.push(Layer::new( - device, - &self.solid, - #[cfg(not(target_arch = "wasm32"))] - &self.gradient, - )); + self.layers + .push(Layer::new(device, &self.solid, &self.gradient)); } let layer = &mut self.layers[self.prepare_layer]; @@ -270,7 +254,6 @@ impl Pipeline { device, queue, &self.solid, - #[cfg(not(target_arch = "wasm32"))] &self.gradient, meshes, transformation, @@ -292,7 +275,6 @@ impl Pipeline { #[cfg(feature = "tracing")] let _ = info_span!("Wgpu::Triangle", "DRAW").entered(); - // Configure render pass { let (attachment, resolve_target, load) = if let Some(blit) = &mut self.blit @@ -309,9 +291,6 @@ impl Pipeline { (target, None, wgpu::LoadOp::Load) }; - #[cfg(feature = "tracing")] - let _ = info_span!("Wgpu::Triangle", "BEGIN_RENDER_PASS").enter(); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("iced_wgpu::triangle render pass"), @@ -329,7 +308,6 @@ impl Pipeline { layer.render( &self.solid, - #[cfg(not(target_arch = "wasm32"))] &self.gradient, meshes, scale_factor, @@ -375,28 +353,49 @@ fn multisample_state( } } -#[derive(Debug, Clone, Copy, encase::ShaderType)] +#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] pub struct Uniforms { - transform: glam::Mat4, + transform: [f32; 16], + /// Uniform values must be 256-aligned; + /// see: [`wgpu::Limits`] `min_uniform_buffer_offset_alignment`. + _padding: [f32; 48], } impl Uniforms { pub fn new(transform: Transformation) -> Self { Self { transform: transform.into(), + _padding: [0.0; 48], } } + + pub fn entry() -> wgpu::BindGroupLayoutEntry { + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: wgpu::BufferSize::new( + mem::size_of::() as u64 + ), + }, + count: None, + } + } + + pub fn min_size() -> Option { + wgpu::BufferSize::new(mem::size_of::() as u64) + } } mod solid { - use crate::buffer::dynamic; - use crate::buffer::r#static::Buffer; + use crate::buffer::Buffer; use crate::graphics::primitive; - use crate::graphics::{Antialiasing, Transformation}; + use crate::graphics::Antialiasing; use crate::triangle; - use encase::ShaderType; - #[derive(Debug)] pub struct Pipeline { pub pipeline: wgpu::RenderPipeline, @@ -406,7 +405,7 @@ mod solid { #[derive(Debug)] pub struct Layer { pub vertices: Buffer, - pub uniforms: dynamic::Buffer, + pub uniforms: Buffer, pub constants: wgpu::BindGroup, } @@ -418,16 +417,19 @@ mod solid { let vertices = Buffer::new( device, "iced_wgpu::triangle::solid vertex buffer", + triangle::INITIAL_VERTEX_COUNT, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, ); - let uniforms = dynamic::Buffer::uniform( + let uniforms = Buffer::new( device, "iced_wgpu::triangle::solid uniforms", + 1, + wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, ); let constants = - Self::bind_group(device, uniforms.raw(), constants_layout); + Self::bind_group(device, &uniforms.raw, constants_layout); Self { vertices, @@ -450,7 +452,7 @@ mod solid { wgpu::BufferBinding { buffer, offset: 0, - size: Some(Uniforms::min_size()), + size: triangle::Uniforms::min_size(), }, ), }], @@ -459,7 +461,6 @@ mod solid { } impl Pipeline { - /// Creates a new [SolidPipeline] using `solid.wgsl` shader. pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, @@ -468,16 +469,7 @@ mod solid { let constants_layout = device.create_bind_group_layout( &wgpu::BindGroupLayoutDescriptor { label: Some("iced_wgpu::triangle::solid bind group layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }], + entries: &[triangle::Uniforms::entry()], }, ); @@ -543,12 +535,10 @@ mod solid { } mod gradient { - use crate::buffer::r#static::Buffer; - use crate::graphics::Antialiasing; + use crate::graphics::{primitive, Antialiasing}; use crate::triangle; - use glam::{IVec4, Vec4}; - use iced_graphics::primitive; + use crate::buffer::Buffer; #[derive(Debug)] pub struct Pipeline { @@ -559,7 +549,7 @@ mod gradient { #[derive(Debug)] pub struct Layer { pub vertices: Buffer, - pub uniforms: dynamic::Buffer, //TODO(shan) + pub uniforms: Buffer, pub constants: wgpu::BindGroup, } @@ -571,65 +561,51 @@ mod gradient { let vertices = Buffer::new( device, "iced_wgpu::triangle::gradient vertex buffer", + triangle::INITIAL_VERTEX_COUNT, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, ); - let uniforms = dynamic::Buffer::uniform( + let uniforms = Buffer::new( device, "iced_wgpu::triangle::gradient uniforms", + 1, + wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, ); - let constants = Self::bind_group( - device, - uniforms.raw(), - storage.raw(), - constants_layout, - ); + let constants = + Self::bind_group(device, &uniforms.raw, constants_layout); Self { vertices, uniforms, - storage, constants, - color_stop_offset: 0, - color_stops_pending_write: Storage { - color_stops: vec![], - }, } } pub fn bind_group( device: &wgpu::Device, uniform_buffer: &wgpu::Buffer, - storage_buffer: &wgpu::Buffer, layout: &wgpu::BindGroupLayout, ) -> wgpu::BindGroup { device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("iced_wgpu::triangle::gradient bind group"), layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer( - wgpu::BufferBinding { - buffer: uniform_buffer, - offset: 0, - size: Some(Uniforms::min_size()), - }, - ), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: storage_buffer.as_entire_binding(), - }, - ], + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + wgpu::BufferBinding { + buffer: uniform_buffer, + offset: 0, + size: triangle::Uniforms::min_size(), + }, + ), + }], }) } } impl Pipeline { - /// Creates a new [GradientPipeline] using `gradient.wgsl` shader. - pub(super) fn new( + pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, antialiasing: Option, @@ -639,16 +615,7 @@ mod gradient { label: Some( "iced_wgpu::triangle::gradient bind group layout", ), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }], + entries: &[triangle::Uniforms::entry()], }, ); @@ -674,18 +641,20 @@ mod gradient { ), }); - let pipeline = - device.create_render_pipeline( - &wgpu::RenderPipelineDescriptor { - label: Some("iced_wgpu::triangle::gradient pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "gradient_vs_main", - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array!( + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::triangle::gradient pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "gradient_vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::< + primitive::GradientVertex2D, + >() + as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array!( // Position 0 => Float32x2, // Color 1 @@ -711,19 +680,19 @@ mod gradient { // Direction 11 => Float32x4 ), - }], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "gradient_fs_main", - targets: &[triangle::fragment_target(format)], - }), - primitive: triangle::primitive_state(), - depth_stencil: None, - multisample: triangle::multisample_state(antialiasing), - multiview: None, + }], }, - ); + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "gradient_fs_main", + targets: &[triangle::fragment_target(format)], + }), + primitive: triangle::primitive_state(), + depth_stencil: None, + multisample: triangle::multisample_state(antialiasing), + multiview: None, + }, + ); Self { pipeline, diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 171c4534d8..bc969daebe 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -4,6 +4,7 @@ pub mod event; mod cursor; mod program; +pub use crate::graphics::Gradient; pub use cursor::Cursor; pub use event::Event; pub use program::Program; From dc36423e1a837abcdbe107c99fb9edff9737e041 Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 22 Mar 2023 19:15:03 -0700 Subject: [PATCH 6/7] Changed linear gradient builder to not have a result. --- core/Cargo.toml | 2 +- core/src/gradient.rs | 63 +++++++++++-------------------- examples/solar_system/src/main.rs | 3 +- examples/tour/src/main.rs | 2 - graphics/src/gradient.rs | 50 +++++++++++------------- tiny_skia/src/backend.rs | 39 ++++++++++--------- tiny_skia/src/geometry.rs | 43 +++++++++++++-------- wgpu/src/geometry.rs | 8 ++-- wgpu/src/layer.rs | 7 ++-- 9 files changed, 106 insertions(+), 111 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index dac3182868..82bb87f704 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -9,7 +9,7 @@ repository = "https://github.com/iced-rs/iced" [dependencies] bitflags = "1.2" -thiserror = "1" +log = "0.4" twox-hash = { version = "1.5", default-features = false } [dependencies.palette] diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 7e90c2d83f..eac9f6f4ec 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -26,7 +26,7 @@ impl Gradient { pub fn mul_alpha(mut self, alpha_multiplier: f32) -> Self { match &mut self { Gradient::Linear(linear) => { - for stop in &mut linear.color_stops { + for stop in linear.color_stops.iter_mut().flatten() { stop.color.a *= alpha_multiplier; } } @@ -54,6 +54,7 @@ pub mod linear { //! Linear gradient builder & definition. use crate::gradient::{ColorStop, Gradient}; use crate::{Color, Radians}; + use std::cmp::Ordering; /// A linear gradient that can be used as a [`Background`]. #[derive(Debug, Clone, Copy, PartialEq)] @@ -61,15 +62,14 @@ pub mod linear { /// How the [`Gradient`] is angled within its bounds. pub angle: Radians, /// [`ColorStop`]s along the linear gradient path. - pub color_stops: [ColorStop; 8], + pub color_stops: [Option; 8], } /// A [`Linear`] builder. #[derive(Debug)] pub struct Builder { angle: Radians, - stops: [ColorStop; 8], - error: Option, + stops: [Option; 8], } impl Builder { @@ -77,11 +77,7 @@ pub mod linear { pub fn new(angle: Radians) -> Self { Self { angle, - stops: std::array::from_fn(|_| ColorStop { - offset: 2.0, //default offset = invalid - color: Default::default(), - }), - error: None, + stops: [None; 8], } } @@ -92,20 +88,27 @@ pub mod linear { /// Any stop added after the 8th will be silently ignored. pub fn add_stop(mut self, offset: f32, color: Color) -> Self { if offset.is_finite() && (0.0..=1.0).contains(&offset) { - match self.stops.binary_search_by(|stop| { - stop.offset.partial_cmp(&offset).unwrap() + match self.stops.binary_search_by(|stop| match stop { + None => Ordering::Greater, + Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), }) { - Ok(_) => { - self.error = Some(BuilderError::DuplicateOffset(offset)) + Ok(index) => { + if index < 8 { + self.stops[index] = + Some(ColorStop { offset, color }); + } } Err(index) => { if index < 8 { - self.stops[index] = ColorStop { offset, color }; + self.stops[index] = + Some(ColorStop { offset, color }); } } } } else { - self.error = Some(BuilderError::InvalidOffset(offset)) + log::warn!( + "Gradient: ColorStop must be within 0.0..=1.0 range." + ); }; self @@ -128,31 +131,11 @@ pub mod linear { /// Builds the linear [`Gradient`] of this [`Builder`]. /// /// Returns `BuilderError` if gradient in invalid. - pub fn build(self) -> Result { - if self.stops.is_empty() { - Err(BuilderError::MissingColorStop) - } else if let Some(error) = self.error { - Err(error) - } else { - Ok(Gradient::Linear(Linear { - angle: self.angle, - color_stops: self.stops, - })) - } + pub fn build(self) -> Gradient { + Gradient::Linear(Linear { + angle: self.angle, + color_stops: self.stops, + }) } } - - /// An error that happened when building a [`Linear`] gradient. - #[derive(Debug, thiserror::Error)] - pub enum BuilderError { - #[error("Gradients must contain at least one color stop.")] - /// Gradients must contain at least one color stop. - MissingColorStop, - #[error("Offset {0} must be a unique, finite number.")] - /// Offsets in a gradient must all be unique & finite. - DuplicateOffset(f32), - #[error("Offset {0} must be between 0.0..=1.0.")] - /// Offsets in a gradient must be between 0.0..=1.0. - InvalidOffset(f32), - } } diff --git a/examples/solar_system/src/main.rs b/examples/solar_system/src/main.rs index c09cd8cfbc..42606e3f96 100644 --- a/examples/solar_system/src/main.rs +++ b/examples/solar_system/src/main.rs @@ -215,8 +215,7 @@ impl canvas::Program for State { ) .add_stop(0.2, Color::from_rgb(0.15, 0.50, 1.0)) .add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47)) - .build() - .expect("Build Earth fill gradient"); + .build(); frame.fill(&earth, earth_fill); diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 13a84ec6f6..0d668cbce3 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -690,7 +690,6 @@ impl widget::button::StyleSheet for CustomButtonStyle { .add_stop(0.0, Palette::LIGHT.primary) .add_stop(1.0, Color::from_rgb8(54, 80, 168)) .build() - .expect("Build gradient") .into(), text_color: Color::WHITE, border_radius: 5.0, @@ -703,7 +702,6 @@ impl widget::button::StyleSheet for CustomButtonStyle { .add_stop(0.0, Color::from_rgb8(194, 194, 194)) .add_stop(1.0, Color::from_rgb8(126, 126, 126)) .build() - .expect("Build gradient") .into(), text_color: Color::WHITE, border_radius: 5.0, diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index adfe414f2d..8ddad8f99d 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -25,9 +25,9 @@ impl Gradient { pub mod linear { //! Linear gradient builder & definition. use crate::Gradient; - use iced_core::gradient::linear::BuilderError; use iced_core::gradient::ColorStop; use iced_core::{Color, Point}; + use std::cmp::Ordering; /// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. /// @@ -42,7 +42,7 @@ pub mod linear { pub end: Point, /// [`ColorStop`]s along the linear gradient path. - pub color_stops: [ColorStop; 8], + pub color_stops: [Option; 8], } /// A [`Linear`] builder. @@ -50,8 +50,7 @@ pub mod linear { pub struct Builder { start: Point, end: Point, - stops: [ColorStop; 8], - error: Option, + stops: [Option; 8], } impl Builder { @@ -60,11 +59,7 @@ pub mod linear { Self { start, end, - stops: std::array::from_fn(|_| ColorStop { - offset: 2.0, //default offset = invalid - color: Default::default(), - }), - error: None, + stops: [None; 8], } } @@ -75,20 +70,27 @@ pub mod linear { /// Any stop added after the 8th will be silently ignored. pub fn add_stop(mut self, offset: f32, color: Color) -> Self { if offset.is_finite() && (0.0..=1.0).contains(&offset) { - match self.stops.binary_search_by(|stop| { - stop.offset.partial_cmp(&offset).unwrap() + match self.stops.binary_search_by(|stop| match stop { + None => Ordering::Greater, + Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), }) { - Ok(_) => { - self.error = Some(BuilderError::DuplicateOffset(offset)) + Ok(index) => { + if index < 8 { + self.stops[index] = + Some(ColorStop { offset, color }); + } } Err(index) => { if index < 8 { - self.stops[index] = ColorStop { offset, color }; + self.stops[index] = + Some(ColorStop { offset, color }); } } } } else { - self.error = Some(BuilderError::InvalidOffset(offset)) + log::warn!( + "Gradient: ColorStop must be within 0.0..=1.0 range." + ); }; self @@ -111,18 +113,12 @@ pub mod linear { /// Builds the linear [`Gradient`] of this [`Builder`]. /// /// Returns `BuilderError` if gradient in invalid. - pub fn build(self) -> Result { - if self.stops.is_empty() { - Err(BuilderError::MissingColorStop) - } else if let Some(error) = self.error { - Err(error) - } else { - Ok(Gradient::Linear(Linear { - start: self.start, - end: self.end, - color_stops: self.stops, - })) - } + pub fn build(self) -> Gradient { + Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: self.stops, + }) } } } diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 7acab649af..2668cf02fd 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -327,6 +327,23 @@ fn into_gradient<'a>( ) -> tiny_skia::Shader<'a> { let Gradient::Linear(linear) = gradient; let (start, end) = linear.angle.to_distance(&bounds); + let stops: Vec = linear + .color_stops + .into_iter() + .flatten() + .map(|stop| { + tiny_skia::GradientStop::new( + stop.offset, + tiny_skia::Color::from_rgba( + stop.color.b, + stop.color.g, + stop.color.r, + stop.color.a, + ) + .expect("Create color"), + ) + }) + .collect(); tiny_skia::LinearGradient::new( tiny_skia::Point { @@ -334,23 +351,11 @@ fn into_gradient<'a>( y: start.y, }, tiny_skia::Point { x: end.x, y: end.y }, - linear - .color_stops - .into_iter() - .filter(|stop| stop.offset <= 1.0) - .map(|stop| { - tiny_skia::GradientStop::new( - stop.offset, - tiny_skia::Color::from_rgba( - stop.color.b, - stop.color.g, - stop.color.r, - stop.color.a, - ) - .expect("Create color"), - ) - }) - .collect(), + if stops.is_empty() { + vec![tiny_skia::GradientStop::new(0.0, tiny_skia::Color::BLACK)] + } else { + stops + }, tiny_skia::SpreadMode::Pad, tiny_skia::Transform::identity(), ) diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index bf03fae375..a8da044040 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -218,19 +218,11 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> { .expect("Create color"), ), Style::Gradient(gradient) => match gradient { - Gradient::Linear(linear) => tiny_skia::LinearGradient::new( - tiny_skia::Point { - x: linear.start.x, - y: linear.start.y, - }, - tiny_skia::Point { - x: linear.end.x, - y: linear.end.y, - }, - linear + Gradient::Linear(linear) => { + let stops: Vec = linear .color_stops .into_iter() - .filter(|stop| stop.offset <= 1.0) + .flatten() .map(|stop| { tiny_skia::GradientStop::new( stop.offset, @@ -243,11 +235,30 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> { .expect("Create color"), ) }) - .collect(), - tiny_skia::SpreadMode::Pad, - tiny_skia::Transform::identity(), - ) - .expect("Create linear gradient"), + .collect(); + + tiny_skia::LinearGradient::new( + tiny_skia::Point { + x: linear.start.x, + y: linear.start.y, + }, + tiny_skia::Point { + x: linear.end.x, + y: linear.end.y, + }, + if stops.is_empty() { + vec![tiny_skia::GradientStop::new( + 0.0, + tiny_skia::Color::BLACK, + )] + } else { + stops + }, + tiny_skia::SpreadMode::Pad, + tiny_skia::Transform::identity(), + ) + .expect("Create linear gradient") + } }, }, anti_alias: true, diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index 44b1a89f57..5e98bcd6ee 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -626,15 +626,17 @@ fn pack_gradient(gradient: &Gradient) -> [f32; 44] { let mut pack: [f32; 44] = [0.0; 44]; let mut offsets: [f32; 8] = [2.0; 8]; - for (index, stop) in linear.color_stops.iter().enumerate().take(8) { - let [r, g, b, a] = stop.color.into_linear(); + for (index, stop) in linear.color_stops.iter().enumerate() { + let [r, g, b, a] = stop + .map_or(crate::core::Color::default(), |s| s.color) + .into_linear(); pack[(index * 4)] = r; pack[(index * 4) + 1] = g; pack[(index * 4) + 2] = b; pack[(index * 4) + 3] = a; - offsets[index] = stop.offset; + offsets[index] = stop.map_or(2.0, |s| s.offset); } pack[32] = offsets[0]; diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index f982818e80..d9a9c82d2f 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -309,14 +309,15 @@ fn pack_gradient( crate::core::Gradient::Linear(linear) => { let mut pack: [f32; 44] = [0.0; 44]; - for (index, stop) in linear.color_stops.iter().enumerate().take(8) { - let [r, g, b, a] = stop.color.into_linear(); + for (index, stop) in linear.color_stops.iter().enumerate() { + let [r, g, b, a] = + stop.map_or(Color::default(), |s| s.color).into_linear(); pack[(index * 4)] = r; pack[(index * 4) + 1] = g; pack[(index * 4) + 2] = b; pack[(index * 4) + 3] = a; - pack[32 + index] = stop.offset; + pack[32 + index] = stop.map_or(2.0, |s| s.offset); } let (start, end) = linear.angle.to_distance(&bounds); From cc06f243cb64c5f0c52829bb5ef299b6fa6fae56 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Thu, 23 Mar 2023 10:52:36 -0700 Subject: [PATCH 7/7] Removed code dupe --- core/src/gradient.rs | 24 ++++++++---------------- graphics/src/gradient.rs | 24 ++++++++---------------- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/core/src/gradient.rs b/core/src/gradient.rs index eac9f6f4ec..d3ed7dd4ec 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -88,22 +88,14 @@ pub mod linear { /// Any stop added after the 8th will be silently ignored. pub fn add_stop(mut self, offset: f32, color: Color) -> Self { if offset.is_finite() && (0.0..=1.0).contains(&offset) { - match self.stops.binary_search_by(|stop| match stop { - None => Ordering::Greater, - Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), - }) { - Ok(index) => { - if index < 8 { - self.stops[index] = - Some(ColorStop { offset, color }); - } - } - Err(index) => { - if index < 8 { - self.stops[index] = - Some(ColorStop { offset, color }); - } - } + let (Ok(index) | Err(index)) = + self.stops.binary_search_by(|stop| match stop { + None => Ordering::Greater, + Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), + }); + + if index < 8 { + self.stops[index] = Some(ColorStop { offset, color }); } } else { log::warn!( diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 8ddad8f99d..05ee983e4a 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -70,22 +70,14 @@ pub mod linear { /// Any stop added after the 8th will be silently ignored. pub fn add_stop(mut self, offset: f32, color: Color) -> Self { if offset.is_finite() && (0.0..=1.0).contains(&offset) { - match self.stops.binary_search_by(|stop| match stop { - None => Ordering::Greater, - Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), - }) { - Ok(index) => { - if index < 8 { - self.stops[index] = - Some(ColorStop { offset, color }); - } - } - Err(index) => { - if index < 8 { - self.stops[index] = - Some(ColorStop { offset, color }); - } - } + let (Ok(index) | Err(index)) = + self.stops.binary_search_by(|stop| match stop { + None => Ordering::Greater, + Some(stop) => stop.offset.partial_cmp(&offset).unwrap(), + }); + + if index < 8 { + self.stops[index] = Some(ColorStop { offset, color }); } } else { log::warn!(