Merge pull request #1846 from bungoboingo/feat/background-gradients
[Feature] Gradients for Backgrounds
This commit is contained in:
commit
cc5d11f1a6
42 changed files with 1580 additions and 1465 deletions
|
|
@ -10,6 +10,7 @@ repository = "https://github.com/iced-rs/iced"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
bitflags = "1.2"
|
bitflags = "1.2"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
log = "0.4.17"
|
||||||
twox-hash = { version = "1.5", default-features = false }
|
twox-hash = { version = "1.5", default-features = false }
|
||||||
|
|
||||||
[dependencies.palette]
|
[dependencies.palette]
|
||||||
|
|
|
||||||
33
core/src/angle.rs
Normal file
33
core/src/angle.rs
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
use crate::{Point, Rectangle, Vector};
|
||||||
|
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<Degrees> for Radians {
|
||||||
|
fn from(degrees: Degrees) -> Self {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,11 +1,14 @@
|
||||||
|
use crate::gradient::{self, Gradient};
|
||||||
use crate::Color;
|
use crate::Color;
|
||||||
|
|
||||||
/// The background of some element.
|
/// The background of some element.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Background {
|
pub enum Background {
|
||||||
/// A solid color
|
/// A solid color.
|
||||||
Color(Color),
|
Color(Color),
|
||||||
// TODO: Add gradient and image variants
|
/// Linearly interpolate between several colors.
|
||||||
|
Gradient(Gradient),
|
||||||
|
// TODO: Add image variant
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for Background {
|
impl From<Color> for Background {
|
||||||
|
|
@ -14,8 +17,14 @@ impl From<Color> for Background {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Color> for Option<Background> {
|
impl From<Gradient> for Background {
|
||||||
fn from(color: Color) -> Self {
|
fn from(gradient: Gradient) -> Self {
|
||||||
Some(Background::from(color))
|
Background::Gradient(gradient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<gradient::Linear> for Background {
|
||||||
|
fn from(gradient: gradient::Linear) -> Self {
|
||||||
|
Background::Gradient(Gradient::Linear(gradient))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,27 +1,40 @@
|
||||||
//! For creating a Gradient.
|
//! Colors that transition progressively.
|
||||||
pub mod linear;
|
use crate::{Color, Radians};
|
||||||
|
|
||||||
pub use linear::Linear;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use crate::{Color, Point, Size};
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
|
||||||
/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD),
|
/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD),
|
||||||
/// or conically (TBD).
|
/// or conically (TBD).
|
||||||
|
///
|
||||||
|
/// For a gradient which can be used as a fill on a canvas, see [`iced_graphics::Gradient`].
|
||||||
pub enum Gradient {
|
pub enum Gradient {
|
||||||
/// A linear gradient interpolates colors along a direction from its `start` to its `end`
|
/// A linear gradient interpolates colors along a direction at a specific [`Angle`].
|
||||||
/// point.
|
|
||||||
Linear(Linear),
|
Linear(Linear),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Gradient {
|
impl Gradient {
|
||||||
/// Creates a new linear [`linear::Builder`].
|
/// Adjust the opacity of the gradient by a multiplier applied to each color stop.
|
||||||
pub fn linear(position: impl Into<Position>) -> linear::Builder {
|
pub fn mul_alpha(mut self, alpha_multiplier: f32) -> Self {
|
||||||
linear::Builder::new(position.into())
|
match &mut self {
|
||||||
|
Gradient::Linear(linear) => {
|
||||||
|
for stop in linear.stops.iter_mut().flatten() {
|
||||||
|
stop.color.a *= alpha_multiplier;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
impl From<Linear> for Gradient {
|
||||||
|
fn from(gradient: Linear) -> Self {
|
||||||
|
Self::Linear(gradient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||||
/// A point along the gradient vector where the specified [`color`] is unmixed.
|
/// A point along the gradient vector where the specified [`color`] is unmixed.
|
||||||
///
|
///
|
||||||
/// [`color`]: Self::color
|
/// [`color`]: Self::color
|
||||||
|
|
@ -35,83 +48,58 @@ pub struct ColorStop {
|
||||||
pub color: Color,
|
pub color: Color,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
/// A linear gradient.
|
||||||
/// The position of the gradient within its bounds.
|
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||||
pub enum Position {
|
pub struct Linear {
|
||||||
/// The gradient will be positioned with respect to two points.
|
/// How the [`Gradient`] is angled within its bounds.
|
||||||
Absolute {
|
pub angle: Radians,
|
||||||
/// The starting point of the gradient.
|
/// [`ColorStop`]s along the linear gradient path.
|
||||||
start: Point,
|
pub stops: [Option<ColorStop>; 8],
|
||||||
/// 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,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<(Point, Point)> for Position {
|
impl Linear {
|
||||||
fn from((start, end): (Point, Point)) -> Self {
|
/// Creates a new [`Linear`] gradient with the given angle in [`Radians`].
|
||||||
Self::Absolute { start, end }
|
pub fn new(angle: impl Into<Radians>) -> Self {
|
||||||
}
|
Self {
|
||||||
}
|
angle: angle.into(),
|
||||||
|
stops: [None; 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,
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
Location::Left => {
|
|
||||||
Point::new(top_left.x, top_left.y + size.height / 2.0)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient.
|
||||||
|
///
|
||||||
|
/// Any `offset` that is not within `0.0..=1.0` will be silently ignored.
|
||||||
|
///
|
||||||
|
/// 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) {
|
||||||
|
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!("Gradient color stop must be within 0.0..=1.0 range.");
|
||||||
|
};
|
||||||
|
|
||||||
|
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<Item = ColorStop>,
|
||||||
|
) -> Self {
|
||||||
|
for stop in stops.into_iter() {
|
||||||
|
self = self.add_stop(stop.offset, stop.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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<ColorStop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A [`Linear`] builder.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Builder {
|
|
||||||
start: Point,
|
|
||||||
end: Point,
|
|
||||||
stops: Vec<ColorStop>,
|
|
||||||
error: Option<BuilderError>,
|
|
||||||
}
|
|
||||||
|
|
||||||
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<Gradient, BuilderError> {
|
|
||||||
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),
|
|
||||||
}
|
|
||||||
|
|
@ -42,6 +42,7 @@ pub mod touch;
|
||||||
pub mod widget;
|
pub mod widget;
|
||||||
pub mod window;
|
pub mod window;
|
||||||
|
|
||||||
|
mod angle;
|
||||||
mod background;
|
mod background;
|
||||||
mod color;
|
mod color;
|
||||||
mod content_fit;
|
mod content_fit;
|
||||||
|
|
@ -57,6 +58,7 @@ mod size;
|
||||||
mod vector;
|
mod vector;
|
||||||
|
|
||||||
pub use alignment::Alignment;
|
pub use alignment::Alignment;
|
||||||
|
pub use angle::{Degrees, Radians};
|
||||||
pub use background::Background;
|
pub use background::Background;
|
||||||
pub use clipboard::Clipboard;
|
pub use clipboard::Clipboard;
|
||||||
pub use color::Color;
|
pub use color::Color;
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,7 @@ pub struct Quad {
|
||||||
pub border_color: Color,
|
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.
|
/// top-left, top-right, bottom-right, bottom-left.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
#[derive(Debug, Clone, Copy, PartialEq, Default)]
|
||||||
pub struct BorderRadius([f32; 4]);
|
pub struct BorderRadius([f32; 4]);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +0,0 @@
|
||||||
[package]
|
|
||||||
name = "modern_art"
|
|
||||||
version = "0.1.0"
|
|
||||||
authors = ["Bingus <shankern@protonmail.com>"]
|
|
||||||
edition = "2021"
|
|
||||||
publish = false
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
|
|
||||||
rand = "0.8.5"
|
|
||||||
env_logger = "0.9"
|
|
||||||
|
|
@ -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<Self::Message>) {
|
|
||||||
(
|
|
||||||
ModernArt {
|
|
||||||
cache: Default::default(),
|
|
||||||
},
|
|
||||||
Command::none(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn title(&self) -> String {
|
|
||||||
String::from("Modern Art")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update(&mut self, _message: Message) -> Command<Message> {
|
|
||||||
Command::none()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn view(&self) -> Element<'_, Self::Message, Renderer<Self::Theme>> {
|
|
||||||
Canvas::new(self)
|
|
||||||
.width(Length::Fill)
|
|
||||||
.height(Length::Fill)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Message> canvas::Program<Message, Renderer> for ModernArt {
|
|
||||||
type State = ();
|
|
||||||
|
|
||||||
fn draw(
|
|
||||||
&self,
|
|
||||||
_state: &Self::State,
|
|
||||||
renderer: &Renderer,
|
|
||||||
_theme: &Theme,
|
|
||||||
bounds: Rectangle,
|
|
||||||
_cursor: Cursor,
|
|
||||||
) -> Vec<Geometry> {
|
|
||||||
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::<bool>();
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
@ -7,4 +7,5 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
|
iced = { path = "../..", features = ["canvas", "tokio", "debug"] }
|
||||||
|
env_logger = "0.10.0"
|
||||||
rand = "0.8.3"
|
rand = "0.8.3"
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ use iced::application;
|
||||||
use iced::executor;
|
use iced::executor;
|
||||||
use iced::theme::{self, Theme};
|
use iced::theme::{self, Theme};
|
||||||
use iced::widget::canvas;
|
use iced::widget::canvas;
|
||||||
use iced::widget::canvas::gradient::{self, Gradient};
|
use iced::widget::canvas::gradient;
|
||||||
use iced::widget::canvas::stroke::{self, Stroke};
|
use iced::widget::canvas::stroke::{self, Stroke};
|
||||||
use iced::widget::canvas::{Cursor, Path};
|
use iced::widget::canvas::{Cursor, Path};
|
||||||
use iced::window;
|
use iced::window;
|
||||||
|
|
@ -22,6 +22,8 @@ use iced::{
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
pub fn main() -> iced::Result {
|
pub fn main() -> iced::Result {
|
||||||
|
env_logger::builder().format_timestamp(None).init();
|
||||||
|
|
||||||
SolarSystem::run(Settings {
|
SolarSystem::run(Settings {
|
||||||
antialiasing: true,
|
antialiasing: true,
|
||||||
..Settings::default()
|
..Settings::default()
|
||||||
|
|
@ -208,15 +210,12 @@ impl<Message> canvas::Program<Message> for State {
|
||||||
|
|
||||||
let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS);
|
let earth = Path::circle(Point::ORIGIN, Self::EARTH_RADIUS);
|
||||||
|
|
||||||
let earth_fill =
|
let earth_fill = gradient::Linear::new(
|
||||||
Gradient::linear(gradient::Position::Absolute {
|
Point::new(-Self::EARTH_RADIUS, 0.0),
|
||||||
start: Point::new(-Self::EARTH_RADIUS, 0.0),
|
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.2, Color::from_rgb(0.15, 0.50, 1.0))
|
.add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47));
|
||||||
.add_stop(0.8, Color::from_rgb(0.0, 0.20, 0.47))
|
|
||||||
.build()
|
|
||||||
.expect("Build Earth fill gradient");
|
|
||||||
|
|
||||||
frame.fill(&earth, earth_fill);
|
frame.fill(&earth, earth_fill);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@ mod toast {
|
||||||
};
|
};
|
||||||
|
|
||||||
container::Appearance {
|
container::Appearance {
|
||||||
background: pair.color.into(),
|
background: Some(pair.color.into()),
|
||||||
text_color: pair.text.into(),
|
text_color: pair.text.into(),
|
||||||
..Default::default()
|
..Default::default()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,4 +7,4 @@ publish = false
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
iced = { path = "../..", features = ["image", "debug"] }
|
iced = { path = "../..", features = ["image", "debug"] }
|
||||||
env_logger = "0.8"
|
env_logger = "0.10.0"
|
||||||
|
|
|
||||||
|
|
@ -62,11 +62,8 @@ impl Sandbox for Tour {
|
||||||
controls = controls.push(horizontal_space(Length::Fill));
|
controls = controls.push(horizontal_space(Length::Fill));
|
||||||
|
|
||||||
if steps.can_continue() {
|
if steps.can_continue() {
|
||||||
controls = controls.push(
|
controls =
|
||||||
button("Next")
|
controls.push(button("Next").on_press(Message::NextPressed));
|
||||||
.on_press(Message::NextPressed)
|
|
||||||
.style(theme::Button::Primary),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let content: Element<_> = column![
|
let content: Element<_> = column![
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
|
||||||
pub use style::Style;
|
pub use style::Style;
|
||||||
pub use text::Text;
|
pub use text::Text;
|
||||||
|
|
||||||
pub use crate::core::gradient::{self, Gradient};
|
pub use crate::gradient::{self, Gradient};
|
||||||
|
|
||||||
use crate::Primitive;
|
use crate::Primitive;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
//! Fill [crate::widget::canvas::Geometry] with a certain style.
|
//! Fill [crate::widget::canvas::Geometry] with a certain style.
|
||||||
use iced_core::{Color, Gradient};
|
|
||||||
|
|
||||||
pub use crate::geometry::Style;
|
pub use crate::geometry::Style;
|
||||||
|
|
||||||
|
use crate::core::Color;
|
||||||
|
use crate::gradient::{self, Gradient};
|
||||||
|
|
||||||
/// The style used to fill geometry.
|
/// The style used to fill geometry.
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Fill {
|
pub struct Fill {
|
||||||
|
|
@ -49,6 +50,15 @@ impl From<Gradient> for Fill {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<gradient::Linear> for Fill {
|
||||||
|
fn from(gradient: gradient::Linear) -> Self {
|
||||||
|
Fill {
|
||||||
|
style: Style::Gradient(Gradient::Linear(gradient)),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The fill rule defines how to determine what is inside and what is outside of
|
/// The fill rule defines how to determine what is inside and what is outside of
|
||||||
/// a shape.
|
/// a shape.
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use iced_core::{Color, Gradient};
|
use crate::core::Color;
|
||||||
|
use crate::geometry::Gradient;
|
||||||
|
|
||||||
/// The coloring style of some drawing.
|
/// The coloring style of some drawing.
|
||||||
#[derive(Debug, Clone, PartialEq)]
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
|
|
||||||
88
graphics/src/gradient.rs
Normal file
88
graphics/src/gradient.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
//! A gradient that can be used as a [`Fill`] for some geometry.
|
||||||
|
//!
|
||||||
|
//! For a gradient that you can use as a background variant for a widget, see [`Gradient`].
|
||||||
|
//!
|
||||||
|
//! [`Gradient`]: crate::core::Gradient;
|
||||||
|
use crate::core::gradient::ColorStop;
|
||||||
|
use crate::core::{Color, Point};
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
/// A fill which linearly interpolates colors along a direction.
|
||||||
|
///
|
||||||
|
/// For a gradient which can be used as a fill for a background of a widget, see [`crate::core::Gradient`].
|
||||||
|
pub enum Gradient {
|
||||||
|
/// A linear gradient interpolates colors along a direction from its `start` to its `end`
|
||||||
|
/// point.
|
||||||
|
Linear(Linear),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Linear> for Gradient {
|
||||||
|
fn from(gradient: Linear) -> Self {
|
||||||
|
Self::Linear(gradient)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`].
|
||||||
|
///
|
||||||
|
/// [`Fill`]: crate::geometry::Fill;
|
||||||
|
/// [`Stroke`]: crate::geometry::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 direction.
|
||||||
|
pub stops: [Option<ColorStop>; 8],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Linear {
|
||||||
|
/// Creates a new [`Builder`].
|
||||||
|
pub fn new(start: Point, end: Point) -> Self {
|
||||||
|
Self {
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
stops: [None; 8],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient.
|
||||||
|
///
|
||||||
|
/// Any `offset` that is not within `0.0..=1.0` will be silently ignored.
|
||||||
|
///
|
||||||
|
/// 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) {
|
||||||
|
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!("Gradient: ColorStop must be within 0.0..=1.0 range.");
|
||||||
|
};
|
||||||
|
|
||||||
|
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<Item = ColorStop>,
|
||||||
|
) -> Self {
|
||||||
|
for stop in stops.into_iter() {
|
||||||
|
self = self.add_stop(stop.offset, stop.color)
|
||||||
|
}
|
||||||
|
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -29,6 +29,7 @@ mod viewport;
|
||||||
pub mod backend;
|
pub mod backend;
|
||||||
pub mod compositor;
|
pub mod compositor;
|
||||||
pub mod damage;
|
pub mod damage;
|
||||||
|
pub mod gradient;
|
||||||
pub mod primitive;
|
pub mod primitive;
|
||||||
pub mod renderer;
|
pub mod renderer;
|
||||||
|
|
||||||
|
|
@ -42,6 +43,7 @@ pub use antialiasing::Antialiasing;
|
||||||
pub use backend::Backend;
|
pub use backend::Backend;
|
||||||
pub use compositor::Compositor;
|
pub use compositor::Compositor;
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
pub use gradient::Gradient;
|
||||||
pub use primitive::Primitive;
|
pub use primitive::Primitive;
|
||||||
pub use renderer::Renderer;
|
pub use renderer::Renderer;
|
||||||
pub use transformation::Transformation;
|
pub use transformation::Transformation;
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ use crate::core::alignment;
|
||||||
use crate::core::image;
|
use crate::core::image;
|
||||||
use crate::core::svg;
|
use crate::core::svg;
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
use crate::core::{Background, Color, Font, Gradient, Rectangle, Size, Vector};
|
use crate::core::{Background, Color, Font, Rectangle, Size, Vector};
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
use bytemuck::{Pod, Zeroable};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
@ -39,7 +39,7 @@ pub enum Primitive {
|
||||||
bounds: Rectangle,
|
bounds: Rectangle,
|
||||||
/// The background of the quad
|
/// The background of the quad
|
||||||
background: Background,
|
background: Background,
|
||||||
/// The border radius of the quad
|
/// The border radii of the quad
|
||||||
border_radius: [f32; 4],
|
border_radius: [f32; 4],
|
||||||
/// The border width of the quad
|
/// The border width of the quad
|
||||||
border_width: f32,
|
border_width: f32,
|
||||||
|
|
@ -81,15 +81,12 @@ pub enum Primitive {
|
||||||
/// It can be used to render many kinds of geometry freely.
|
/// It can be used to render many kinds of geometry freely.
|
||||||
GradientMesh {
|
GradientMesh {
|
||||||
/// The vertices and indices of the mesh.
|
/// The vertices and indices of the mesh.
|
||||||
buffers: Mesh2D<Vertex2D>,
|
buffers: Mesh2D<GradientVertex2D>,
|
||||||
|
|
||||||
/// The size of the drawable region of the mesh.
|
/// The size of the drawable region of the mesh.
|
||||||
///
|
///
|
||||||
/// Any geometry that falls out of this region will be clipped.
|
/// Any geometry that falls out of this region will be clipped.
|
||||||
size: Size,
|
size: Size,
|
||||||
|
|
||||||
/// The [`Gradient`] to apply to the mesh.
|
|
||||||
gradient: Gradient,
|
|
||||||
},
|
},
|
||||||
/// A [`tiny_skia`] path filled with some paint.
|
/// A [`tiny_skia`] path filled with some paint.
|
||||||
#[cfg(feature = "tiny-skia")]
|
#[cfg(feature = "tiny-skia")]
|
||||||
|
|
@ -242,14 +239,6 @@ pub struct Mesh2D<T> {
|
||||||
pub indices: Vec<u32>,
|
pub indices: Vec<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A two-dimensional vertex.
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
|
|
||||||
#[repr(C)]
|
|
||||||
pub struct Vertex2D {
|
|
||||||
/// The vertex position in 2D space.
|
|
||||||
pub position: [f32; 2],
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A two-dimensional vertex with a color.
|
/// A two-dimensional vertex with a color.
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
|
#[derive(Copy, Clone, Debug, PartialEq, Zeroable, Pod)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
|
@ -261,6 +250,23 @@ pub struct ColoredVertex2D {
|
||||||
pub color: [f32; 4],
|
pub color: [f32; 4],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A vertex which contains 2D position & packed gradient data.
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct GradientVertex2D {
|
||||||
|
/// The vertex position in 2D space.
|
||||||
|
pub position: [f32; 2],
|
||||||
|
|
||||||
|
/// 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 {
|
impl From<()> for Primitive {
|
||||||
fn from(_: ()) -> Self {
|
fn from(_: ()) -> Self {
|
||||||
Self::Group { primitives: vec![] }
|
Self::Group { primitives: vec![] }
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
//! Draw geometry using meshes of triangles.
|
|
||||||
|
|
@ -188,9 +188,10 @@ pub use style::theme;
|
||||||
|
|
||||||
pub use crate::core::alignment;
|
pub use crate::core::alignment;
|
||||||
pub use crate::core::event;
|
pub use crate::core::event;
|
||||||
|
pub use crate::core::gradient;
|
||||||
pub use crate::core::{
|
pub use crate::core::{
|
||||||
color, Alignment, Background, Color, ContentFit, Length, Padding, Pixels,
|
color, Alignment, Background, Color, ContentFit, Degrees, Gradient, Length,
|
||||||
Point, Rectangle, Size, Vector,
|
Padding, Pixels, Point, Radians, Rectangle, Size, Vector,
|
||||||
};
|
};
|
||||||
pub use crate::runtime::Command;
|
pub use crate::runtime::Command;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -68,6 +68,9 @@ pub trait StyleSheet {
|
||||||
a: color.a * 0.5,
|
a: color.a * 0.5,
|
||||||
..color
|
..color
|
||||||
}),
|
}),
|
||||||
|
Background::Gradient(gradient) => {
|
||||||
|
Background::Gradient(gradient.mul_alpha(0.5))
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
text_color: Color {
|
text_color: Color {
|
||||||
a: active.text_color.a * 0.5,
|
a: active.text_color.a * 0.5,
|
||||||
|
|
|
||||||
|
|
@ -139,6 +139,15 @@ pub enum Button {
|
||||||
Custom(Box<dyn button::StyleSheet<Style = Theme>>),
|
Custom(Box<dyn button::StyleSheet<Style = Theme>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Button {
|
||||||
|
/// Creates a custom [`Button`] style variant.
|
||||||
|
pub fn custom(
|
||||||
|
style_sheet: impl button::StyleSheet<Style = Theme> + 'static,
|
||||||
|
) -> Self {
|
||||||
|
Self::Custom(Box::new(style_sheet))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl button::StyleSheet for Theme {
|
impl button::StyleSheet for Theme {
|
||||||
type Style = Button;
|
type Style = Button;
|
||||||
|
|
||||||
|
|
@ -217,6 +226,9 @@ impl button::StyleSheet for Theme {
|
||||||
a: color.a * 0.5,
|
a: color.a * 0.5,
|
||||||
..color
|
..color
|
||||||
}),
|
}),
|
||||||
|
Background::Gradient(gradient) => {
|
||||||
|
Background::Gradient(gradient.mul_alpha(0.5))
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
text_color: Color {
|
text_color: Color {
|
||||||
a: active.text_color.a * 0.5,
|
a: active.text_color.a * 0.5,
|
||||||
|
|
@ -368,7 +380,7 @@ impl container::StyleSheet for Theme {
|
||||||
|
|
||||||
container::Appearance {
|
container::Appearance {
|
||||||
text_color: None,
|
text_color: None,
|
||||||
background: palette.background.weak.color.into(),
|
background: Some(palette.background.weak.color.into()),
|
||||||
border_radius: 2.0,
|
border_radius: 2.0,
|
||||||
border_width: 0.0,
|
border_width: 0.0,
|
||||||
border_color: Color::TRANSPARENT,
|
border_color: Color::TRANSPARENT,
|
||||||
|
|
@ -893,7 +905,7 @@ impl scrollable::StyleSheet for Theme {
|
||||||
let palette = self.extended_palette();
|
let palette = self.extended_palette();
|
||||||
|
|
||||||
scrollable::Scrollbar {
|
scrollable::Scrollbar {
|
||||||
background: palette.background.weak.color.into(),
|
background: Some(palette.background.weak.color.into()),
|
||||||
border_radius: 2.0,
|
border_radius: 2.0,
|
||||||
border_width: 0.0,
|
border_width: 0.0,
|
||||||
border_color: Color::TRANSPARENT,
|
border_color: Color::TRANSPARENT,
|
||||||
|
|
@ -920,7 +932,7 @@ impl scrollable::StyleSheet for Theme {
|
||||||
let palette = self.extended_palette();
|
let palette = self.extended_palette();
|
||||||
|
|
||||||
scrollable::Scrollbar {
|
scrollable::Scrollbar {
|
||||||
background: palette.background.weak.color.into(),
|
background: Some(palette.background.weak.color.into()),
|
||||||
border_radius: 2.0,
|
border_radius: 2.0,
|
||||||
border_width: 0.0,
|
border_width: 0.0,
|
||||||
border_color: Color::TRANSPARENT,
|
border_color: Color::TRANSPARENT,
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
use crate::core::text;
|
use crate::core::text;
|
||||||
|
use crate::core::Gradient;
|
||||||
use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector};
|
use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector};
|
||||||
use crate::graphics::backend;
|
use crate::graphics::backend;
|
||||||
use crate::graphics::{Primitive, Viewport};
|
use crate::graphics::{Primitive, Viewport};
|
||||||
|
|
@ -184,6 +185,48 @@ impl Backend {
|
||||||
*color,
|
*color,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
Background::Gradient(Gradient::Linear(linear)) => {
|
||||||
|
let (start, end) =
|
||||||
|
linear.angle.to_distance(bounds);
|
||||||
|
|
||||||
|
let stops: Vec<tiny_skia::GradientStop> =
|
||||||
|
linear
|
||||||
|
.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 {
|
||||||
|
x: start.x,
|
||||||
|
y: start.y,
|
||||||
|
},
|
||||||
|
tiny_skia::Point { x: end.x, y: 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,
|
anti_alias: true,
|
||||||
..tiny_skia::Paint::default()
|
..tiny_skia::Paint::default()
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use crate::core::Gradient;
|
|
||||||
use crate::core::{Point, Rectangle, Size, Vector};
|
use crate::core::{Point, Rectangle, Size, Vector};
|
||||||
use crate::graphics::geometry::fill::{self, Fill};
|
use crate::graphics::geometry::fill::{self, Fill};
|
||||||
use crate::graphics::geometry::stroke::{self, Stroke};
|
use crate::graphics::geometry::stroke::{self, Stroke};
|
||||||
use crate::graphics::geometry::{Path, Style, Text};
|
use crate::graphics::geometry::{Path, Style, Text};
|
||||||
|
use crate::graphics::Gradient;
|
||||||
use crate::graphics::Primitive;
|
use crate::graphics::Primitive;
|
||||||
|
|
||||||
pub struct Frame {
|
pub struct Frame {
|
||||||
|
|
@ -231,18 +231,11 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> {
|
||||||
.expect("Create color"),
|
.expect("Create color"),
|
||||||
),
|
),
|
||||||
Style::Gradient(gradient) => match gradient {
|
Style::Gradient(gradient) => match gradient {
|
||||||
Gradient::Linear(linear) => tiny_skia::LinearGradient::new(
|
Gradient::Linear(linear) => {
|
||||||
tiny_skia::Point {
|
let stops: Vec<tiny_skia::GradientStop> = linear
|
||||||
x: linear.start.x,
|
.stops
|
||||||
y: linear.start.y,
|
|
||||||
},
|
|
||||||
tiny_skia::Point {
|
|
||||||
x: linear.end.x,
|
|
||||||
y: linear.end.y,
|
|
||||||
},
|
|
||||||
linear
|
|
||||||
.color_stops
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
.map(|stop| {
|
.map(|stop| {
|
||||||
tiny_skia::GradientStop::new(
|
tiny_skia::GradientStop::new(
|
||||||
stop.offset,
|
stop.offset,
|
||||||
|
|
@ -255,11 +248,30 @@ pub fn into_paint(style: Style) -> tiny_skia::Paint<'static> {
|
||||||
.expect("Create color"),
|
.expect("Create color"),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
.collect(),
|
.collect();
|
||||||
tiny_skia::SpreadMode::Pad,
|
|
||||||
tiny_skia::Transform::identity(),
|
tiny_skia::LinearGradient::new(
|
||||||
)
|
tiny_skia::Point {
|
||||||
.expect("Create linear gradient"),
|
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,
|
anti_alias: true,
|
||||||
|
|
|
||||||
|
|
@ -46,10 +46,6 @@ version = "0.2"
|
||||||
git = "https://github.com/hecrj/glyphon.git"
|
git = "https://github.com/hecrj/glyphon.git"
|
||||||
rev = "f145067d292082abdd1f2b2481812d4a52c394ec"
|
rev = "f145067d292082abdd1f2b2481812d4a52c394ec"
|
||||||
|
|
||||||
[dependencies.encase]
|
|
||||||
version = "0.3.0"
|
|
||||||
features = ["glam"]
|
|
||||||
|
|
||||||
[dependencies.glam]
|
[dependencies.glam]
|
||||||
version = "0.21.3"
|
version = "0.21.3"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,3 @@
|
||||||
//! Utilities for buffer operations.
|
|
||||||
pub mod dynamic;
|
|
||||||
pub mod r#static;
|
|
||||||
|
|
||||||
use std::marker::PhantomData;
|
use std::marker::PhantomData;
|
||||||
use std::ops::RangeBounds;
|
use std::ops::RangeBounds;
|
||||||
|
|
||||||
|
|
@ -10,7 +6,8 @@ pub struct Buffer<T> {
|
||||||
label: &'static str,
|
label: &'static str,
|
||||||
size: u64,
|
size: u64,
|
||||||
usage: wgpu::BufferUsages,
|
usage: wgpu::BufferUsages,
|
||||||
raw: wgpu::Buffer,
|
pub(crate) raw: wgpu::Buffer,
|
||||||
|
offsets: Vec<wgpu::BufferAddress>,
|
||||||
type_: PhantomData<T>,
|
type_: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -35,6 +32,7 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
size,
|
size,
|
||||||
usage,
|
usage,
|
||||||
raw,
|
raw,
|
||||||
|
offsets: Vec::new(),
|
||||||
type_: PhantomData,
|
type_: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -43,6 +41,8 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
let new_size = (std::mem::size_of::<T>() * new_count) as u64;
|
let new_size = (std::mem::size_of::<T>() * new_count) as u64;
|
||||||
|
|
||||||
if self.size < new_size {
|
if self.size < new_size {
|
||||||
|
self.offsets.clear();
|
||||||
|
|
||||||
self.raw = device.create_buffer(&wgpu::BufferDescriptor {
|
self.raw = device.create_buffer(&wgpu::BufferDescriptor {
|
||||||
label: Some(self.label),
|
label: Some(self.label),
|
||||||
size: new_size,
|
size: new_size,
|
||||||
|
|
@ -58,17 +58,19 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the size of the written bytes.
|
||||||
pub fn write(
|
pub fn write(
|
||||||
&self,
|
&mut self,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
offset_count: usize,
|
offset: usize,
|
||||||
contents: &[T],
|
contents: &[T],
|
||||||
) {
|
) -> usize {
|
||||||
queue.write_buffer(
|
let bytes: &[u8] = bytemuck::cast_slice(contents);
|
||||||
&self.raw,
|
queue.write_buffer(&self.raw, offset as u64, bytes);
|
||||||
(std::mem::size_of::<T>() * offset_count) as u64,
|
|
||||||
bytemuck::cast_slice(contents),
|
self.offsets.push(offset as u64);
|
||||||
);
|
|
||||||
|
bytes.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn slice(
|
pub fn slice(
|
||||||
|
|
@ -77,6 +79,21 @@ impl<T: bytemuck::Pod> Buffer<T> {
|
||||||
) -> wgpu::BufferSlice<'_> {
|
) -> wgpu::BufferSlice<'_> {
|
||||||
self.raw.slice(bounds)
|
self.raw.slice(bounds)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the slice calculated from the offset stored at the given index.
|
||||||
|
pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> {
|
||||||
|
self.raw.slice(self.offset_at(index)..)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears any temporary data (i.e. offsets) from the buffer.
|
||||||
|
pub fn clear(&mut self) {
|
||||||
|
self.offsets.clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_copy_size<T>(amount: usize) -> u64 {
|
fn next_copy_size<T>(amount: usize) -> u64 {
|
||||||
|
|
|
||||||
|
|
@ -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<T: ShaderType> {
|
|
||||||
offsets: Vec<wgpu::DynamicOffset>,
|
|
||||||
cpu: Internal,
|
|
||||||
gpu: wgpu::Buffer,
|
|
||||||
label: &'static str,
|
|
||||||
size: u64,
|
|
||||||
_data: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: ShaderType + WriteInto> Buffer<T> {
|
|
||||||
/// 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::<T>::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::<T>::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<Vec<u8>>),
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
//storage buffers are not supported on wgpu wasm target (yet)
|
|
||||||
Storage(encase::DynamicStorageBuffer<Vec<u8>>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Internal {
|
|
||||||
/// Writes the current value to its CPU buffer with proper alignment.
|
|
||||||
pub(super) fn write<T: ShaderType + WriteInto>(
|
|
||||||
&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(_)"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -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<T> {
|
|
||||||
//stored sequentially per mesh iteration; refers to the offset index in the GPU buffer
|
|
||||||
offsets: Vec<wgpu::BufferAddress>,
|
|
||||||
label: &'static str,
|
|
||||||
usages: wgpu::BufferUsages,
|
|
||||||
gpu: wgpu::Buffer,
|
|
||||||
size: wgpu::BufferAddress,
|
|
||||||
_data: PhantomData<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Pod + Zeroable> Buffer<T> {
|
|
||||||
/// Initialize a new static buffer.
|
|
||||||
pub fn new(
|
|
||||||
device: &wgpu::Device,
|
|
||||||
label: &'static str,
|
|
||||||
usages: wgpu::BufferUsages,
|
|
||||||
) -> Self {
|
|
||||||
let size = (mem::size_of::<T>() 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::<T>() * new_count) as u64;
|
|
||||||
|
|
||||||
if self.size < size {
|
|
||||||
self.size =
|
|
||||||
(mem::size_of::<T>() * (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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
//! Build and draw geometry.
|
//! Build and draw geometry.
|
||||||
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::fill::{self, Fill};
|
||||||
use crate::graphics::geometry::{
|
use crate::graphics::geometry::{
|
||||||
LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
|
LineCap, LineDash, LineJoin, Path, Stroke, Style, Text,
|
||||||
};
|
};
|
||||||
use crate::graphics::primitive::{self, Primitive};
|
use crate::graphics::primitive::{self, Primitive};
|
||||||
|
use crate::graphics::Gradient;
|
||||||
|
|
||||||
use lyon::geom::euclid;
|
use lyon::geom::euclid;
|
||||||
use lyon::tessellation;
|
use lyon::tessellation;
|
||||||
|
|
@ -23,10 +24,7 @@ pub struct Frame {
|
||||||
|
|
||||||
enum Buffer {
|
enum Buffer {
|
||||||
Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),
|
Solid(tessellation::VertexBuffers<primitive::ColoredVertex2D, u32>),
|
||||||
Gradient(
|
Gradient(tessellation::VertexBuffers<primitive::GradientVertex2D, u32>),
|
||||||
tessellation::VertexBuffers<primitive::Vertex2D, u32>,
|
|
||||||
Gradient,
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct BufferStack {
|
struct BufferStack {
|
||||||
|
|
@ -48,12 +46,11 @@ impl BufferStack {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Style::Gradient(gradient) => match self.stack.last() {
|
Style::Gradient(_) => match self.stack.last() {
|
||||||
Some(Buffer::Gradient(_, last)) if gradient == last => {}
|
Some(Buffer::Gradient(_)) => {}
|
||||||
_ => {
|
_ => {
|
||||||
self.stack.push(Buffer::Gradient(
|
self.stack.push(Buffer::Gradient(
|
||||||
tessellation::VertexBuffers::new(),
|
tessellation::VertexBuffers::new(),
|
||||||
gradient.clone(),
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -73,9 +70,14 @@ impl BufferStack {
|
||||||
TriangleVertex2DBuilder(color.into_linear()),
|
TriangleVertex2DBuilder(color.into_linear()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
(Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new(
|
(Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
|
||||||
tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder),
|
Box::new(tessellation::BuffersBuilder::new(
|
||||||
),
|
buffer,
|
||||||
|
GradientVertex2DBuilder {
|
||||||
|
gradient: pack_gradient(gradient),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -91,9 +93,14 @@ impl BufferStack {
|
||||||
TriangleVertex2DBuilder(color.into_linear()),
|
TriangleVertex2DBuilder(color.into_linear()),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
(Style::Gradient(_), Buffer::Gradient(buffer, _)) => Box::new(
|
(Style::Gradient(gradient), Buffer::Gradient(buffer)) => {
|
||||||
tessellation::BuffersBuilder::new(buffer, Vertex2DBuilder),
|
Box::new(tessellation::BuffersBuilder::new(
|
||||||
),
|
buffer,
|
||||||
|
GradientVertex2DBuilder {
|
||||||
|
gradient: pack_gradient(gradient),
|
||||||
|
},
|
||||||
|
))
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -131,11 +138,13 @@ impl Transform {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
|
fn transform_gradient(&self, mut gradient: Gradient) -> Gradient {
|
||||||
let (start, end) = match &mut gradient {
|
match &mut gradient {
|
||||||
Gradient::Linear(linear) => (&mut linear.start, &mut linear.end),
|
Gradient::Linear(linear) => {
|
||||||
};
|
self.transform_point(&mut linear.start);
|
||||||
self.transform_point(start);
|
self.transform_point(&mut linear.end);
|
||||||
self.transform_point(end);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
gradient
|
gradient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -462,7 +471,7 @@ impl Frame {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Buffer::Gradient(buffer, gradient) => {
|
Buffer::Gradient(buffer) => {
|
||||||
if !buffer.indices.is_empty() {
|
if !buffer.indices.is_empty() {
|
||||||
self.primitives.push(Primitive::GradientMesh {
|
self.primitives.push(Primitive::GradientMesh {
|
||||||
buffers: primitive::Mesh2D {
|
buffers: primitive::Mesh2D {
|
||||||
|
|
@ -470,7 +479,6 @@ impl Frame {
|
||||||
indices: buffer.indices,
|
indices: buffer.indices,
|
||||||
},
|
},
|
||||||
size: self.size,
|
size: self.size,
|
||||||
gradient,
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -481,34 +489,38 @@ impl Frame {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Vertex2DBuilder;
|
struct GradientVertex2DBuilder {
|
||||||
|
gradient: [f32; 44],
|
||||||
|
}
|
||||||
|
|
||||||
impl tessellation::FillVertexConstructor<primitive::Vertex2D>
|
impl tessellation::FillVertexConstructor<primitive::GradientVertex2D>
|
||||||
for Vertex2DBuilder
|
for GradientVertex2DBuilder
|
||||||
{
|
{
|
||||||
fn new_vertex(
|
fn new_vertex(
|
||||||
&mut self,
|
&mut self,
|
||||||
vertex: tessellation::FillVertex<'_>,
|
vertex: tessellation::FillVertex<'_>,
|
||||||
) -> primitive::Vertex2D {
|
) -> primitive::GradientVertex2D {
|
||||||
let position = vertex.position();
|
let position = vertex.position();
|
||||||
|
|
||||||
primitive::Vertex2D {
|
primitive::GradientVertex2D {
|
||||||
position: [position.x, position.y],
|
position: [position.x, position.y],
|
||||||
|
gradient: self.gradient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl tessellation::StrokeVertexConstructor<primitive::Vertex2D>
|
impl tessellation::StrokeVertexConstructor<primitive::GradientVertex2D>
|
||||||
for Vertex2DBuilder
|
for GradientVertex2DBuilder
|
||||||
{
|
{
|
||||||
fn new_vertex(
|
fn new_vertex(
|
||||||
&mut self,
|
&mut self,
|
||||||
vertex: tessellation::StrokeVertex<'_, '_>,
|
vertex: tessellation::StrokeVertex<'_, '_>,
|
||||||
) -> primitive::Vertex2D {
|
) -> primitive::GradientVertex2D {
|
||||||
let position = vertex.position();
|
let position = vertex.position();
|
||||||
|
|
||||||
primitive::Vertex2D {
|
primitive::GradientVertex2D {
|
||||||
position: [position.x, position.y],
|
position: [position.x, position.y],
|
||||||
|
gradient: self.gradient,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -611,3 +623,42 @@ 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.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.map_or(2.0, |s| s.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -121,7 +121,7 @@ impl Layer {
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = self.instances.resize(device, instances.len());
|
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();
|
self.instance_count = instances.len();
|
||||||
}
|
}
|
||||||
|
|
@ -278,7 +278,7 @@ impl Pipeline {
|
||||||
let vertices =
|
let vertices =
|
||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("iced_wgpu::image vertex buffer"),
|
label: Some("iced_wgpu::image vertex buffer"),
|
||||||
contents: bytemuck::cast_slice(&QUAD_VERTS),
|
contents: bytemuck::cast_slice(&QUAD_VERTICES),
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -498,7 +498,7 @@ pub struct Vertex {
|
||||||
|
|
||||||
const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||||
|
|
||||||
const QUAD_VERTS: [Vertex; 4] = [
|
const QUAD_VERTICES: [Vertex; 4] = [
|
||||||
Vertex {
|
Vertex {
|
||||||
_position: [0.0, 0.0],
|
_position: [0.0, 0.0],
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
//! Organize rendering primitives into a flattened list of layers.
|
//! Organize rendering primitives into a flattened list of layers.
|
||||||
mod image;
|
mod image;
|
||||||
mod quad;
|
|
||||||
mod text;
|
mod text;
|
||||||
|
|
||||||
pub mod mesh;
|
pub mod mesh;
|
||||||
|
pub mod quad;
|
||||||
|
|
||||||
pub use image::Image;
|
pub use image::Image;
|
||||||
pub use mesh::Mesh;
|
pub use mesh::Mesh;
|
||||||
|
|
@ -22,7 +22,7 @@ pub struct Layer<'a> {
|
||||||
pub bounds: Rectangle,
|
pub bounds: Rectangle,
|
||||||
|
|
||||||
/// The quads of the [`Layer`].
|
/// The quads of the [`Layer`].
|
||||||
pub quads: Vec<Quad>,
|
pub quads: Quads,
|
||||||
|
|
||||||
/// The triangle meshes of the [`Layer`].
|
/// The triangle meshes of the [`Layer`].
|
||||||
pub meshes: Vec<Mesh<'a>>,
|
pub meshes: Vec<Mesh<'a>>,
|
||||||
|
|
@ -34,12 +34,29 @@ pub struct Layer<'a> {
|
||||||
pub images: Vec<Image>,
|
pub images: Vec<Image>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The quads of the [`Layer`].
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct Quads {
|
||||||
|
/// The solid quads of the [`Layer`].
|
||||||
|
pub solids: Vec<quad::Solid>,
|
||||||
|
|
||||||
|
/// The gradient quads of the [`Layer`].
|
||||||
|
pub gradients: Vec<quad::Gradient>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Quads {
|
||||||
|
/// Returns true if there are no quads of any type in [`Quads`].
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.solids.is_empty() && self.gradients.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> Layer<'a> {
|
impl<'a> Layer<'a> {
|
||||||
/// Creates a new [`Layer`] with the given clipping bounds.
|
/// Creates a new [`Layer`] with the given clipping bounds.
|
||||||
pub fn new(bounds: Rectangle) -> Self {
|
pub fn new(bounds: Rectangle) -> Self {
|
||||||
Self {
|
Self {
|
||||||
bounds,
|
bounds,
|
||||||
quads: Vec::new(),
|
quads: Quads::default(),
|
||||||
meshes: Vec::new(),
|
meshes: Vec::new(),
|
||||||
text: Vec::new(),
|
text: Vec::new(),
|
||||||
images: Vec::new(),
|
images: Vec::new(),
|
||||||
|
|
@ -145,20 +162,39 @@ impl<'a> Layer<'a> {
|
||||||
} => {
|
} => {
|
||||||
let layer = &mut layers[current_layer];
|
let layer = &mut layers[current_layer];
|
||||||
|
|
||||||
// TODO: Move some of these computations to the GPU (?)
|
let quad = Quad {
|
||||||
layer.quads.push(Quad {
|
|
||||||
position: [
|
position: [
|
||||||
bounds.x + translation.x,
|
bounds.x + translation.x,
|
||||||
bounds.y + translation.y,
|
bounds.y + translation.y,
|
||||||
],
|
],
|
||||||
size: [bounds.width, bounds.height],
|
size: [bounds.width, bounds.height],
|
||||||
color: match background {
|
border_color: border_color.into_linear(),
|
||||||
Background::Color(color) => color.into_linear(),
|
|
||||||
},
|
|
||||||
border_radius: *border_radius,
|
border_radius: *border_radius,
|
||||||
border_width: *border_width,
|
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(),
|
||||||
|
quad,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Background::Gradient(gradient) => {
|
||||||
|
let quad = quad::Gradient {
|
||||||
|
gradient: pack_gradient(
|
||||||
|
gradient,
|
||||||
|
Rectangle::new(
|
||||||
|
quad.position.into(),
|
||||||
|
quad.size.into(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
quad,
|
||||||
|
};
|
||||||
|
|
||||||
|
layer.quads.gradients.push(quad);
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
Primitive::Image { handle, bounds } => {
|
Primitive::Image { handle, bounds } => {
|
||||||
let layer = &mut layers[current_layer];
|
let layer = &mut layers[current_layer];
|
||||||
|
|
@ -198,11 +234,7 @@ impl<'a> Layer<'a> {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Primitive::GradientMesh {
|
Primitive::GradientMesh { buffers, size } => {
|
||||||
buffers,
|
|
||||||
size,
|
|
||||||
gradient,
|
|
||||||
} => {
|
|
||||||
let layer = &mut layers[current_layer];
|
let layer = &mut layers[current_layer];
|
||||||
|
|
||||||
let bounds = Rectangle::new(
|
let bounds = Rectangle::new(
|
||||||
|
|
@ -216,7 +248,6 @@ impl<'a> Layer<'a> {
|
||||||
origin: Point::new(translation.x, translation.y),
|
origin: Point::new(translation.x, translation.y),
|
||||||
buffers,
|
buffers,
|
||||||
clip_bounds,
|
clip_bounds,
|
||||||
gradient,
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -279,3 +310,32 @@ impl<'a> Layer<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Packs the [`Gradient`] for use in shader code.
|
||||||
|
fn pack_gradient(gradient: &core::Gradient, bounds: Rectangle) -> [f32; 44] {
|
||||||
|
match gradient {
|
||||||
|
core::Gradient::Linear(linear) => {
|
||||||
|
let mut pack: [f32; 44] = [0.0; 44];
|
||||||
|
|
||||||
|
for (index, stop) in linear.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.map_or(2.0, |s| s.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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
//! A collection of triangle primitives.
|
//! A collection of triangle primitives.
|
||||||
use crate::core::{Gradient, Point, Rectangle};
|
use crate::core::{Point, Rectangle};
|
||||||
use crate::graphics::primitive;
|
use crate::graphics::primitive;
|
||||||
|
|
||||||
/// A mesh of triangles.
|
/// A mesh of triangles.
|
||||||
|
|
@ -22,13 +22,10 @@ pub enum Mesh<'a> {
|
||||||
origin: Point,
|
origin: Point,
|
||||||
|
|
||||||
/// The vertex and index buffers of the [`Mesh`].
|
/// The vertex and index buffers of the [`Mesh`].
|
||||||
buffers: &'a primitive::Mesh2D<primitive::Vertex2D>,
|
buffers: &'a primitive::Mesh2D<primitive::GradientVertex2D>,
|
||||||
|
|
||||||
/// The clipping bounds of the [`Mesh`].
|
/// The clipping bounds of the [`Mesh`].
|
||||||
clip_bounds: Rectangle<f32>,
|
clip_bounds: Rectangle<f32>,
|
||||||
|
|
||||||
/// The gradient to apply to the [`Mesh`].
|
|
||||||
gradient: &'a Gradient,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -65,9 +62,15 @@ pub struct AttributeCount {
|
||||||
/// The total amount of solid vertices.
|
/// The total amount of solid vertices.
|
||||||
pub solid_vertices: usize,
|
pub solid_vertices: usize,
|
||||||
|
|
||||||
|
/// The total amount of solid meshes.
|
||||||
|
pub solids: usize,
|
||||||
|
|
||||||
/// The total amount of gradient vertices.
|
/// The total amount of gradient vertices.
|
||||||
pub gradient_vertices: usize,
|
pub gradient_vertices: usize,
|
||||||
|
|
||||||
|
/// The total amount of gradient meshes.
|
||||||
|
pub gradients: usize,
|
||||||
|
|
||||||
/// The total amount of indices.
|
/// The total amount of indices.
|
||||||
pub indices: usize,
|
pub indices: usize,
|
||||||
}
|
}
|
||||||
|
|
@ -79,10 +82,12 @@ pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> AttributeCount {
|
||||||
.fold(AttributeCount::default(), |mut count, mesh| {
|
.fold(AttributeCount::default(), |mut count, mesh| {
|
||||||
match mesh {
|
match mesh {
|
||||||
Mesh::Solid { buffers, .. } => {
|
Mesh::Solid { buffers, .. } => {
|
||||||
|
count.solids += 1;
|
||||||
count.solid_vertices += buffers.vertices.len();
|
count.solid_vertices += buffers.vertices.len();
|
||||||
count.indices += buffers.indices.len();
|
count.indices += buffers.indices.len();
|
||||||
}
|
}
|
||||||
Mesh::Gradient { buffers, .. } => {
|
Mesh::Gradient { buffers, .. } => {
|
||||||
|
count.gradients += 1;
|
||||||
count.gradient_vertices += buffers.vertices.len();
|
count.gradient_vertices += buffers.vertices.len();
|
||||||
count.indices += buffers.indices.len();
|
count.indices += buffers.indices.len();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
/// A colored rectangle with a border.
|
//! A rectangle with certain styled properties.
|
||||||
///
|
|
||||||
/// This type can be directly uploaded to GPU memory.
|
use bytemuck::{Pod, Zeroable};
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
|
/// The properties of a quad.
|
||||||
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
pub struct Quad {
|
pub struct Quad {
|
||||||
/// The position of the [`Quad`].
|
/// The position of the [`Quad`].
|
||||||
|
|
@ -10,21 +12,40 @@ pub struct Quad {
|
||||||
/// The size of the [`Quad`].
|
/// The size of the [`Quad`].
|
||||||
pub size: [f32; 2],
|
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],
|
pub border_color: [f32; 4],
|
||||||
|
|
||||||
/// The border radius of the [`Quad`].
|
/// The border radii of the [`Quad`].
|
||||||
pub border_radius: [f32; 4],
|
pub border_radius: [f32; 4],
|
||||||
|
|
||||||
/// The border width of the [`Quad`].
|
/// The border width of the [`Quad`].
|
||||||
pub border_width: f32,
|
pub border_width: f32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
/// A quad filled with a solid color.
|
||||||
unsafe impl bytemuck::Zeroable for Quad {}
|
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Solid {
|
||||||
|
/// The background color data of the quad.
|
||||||
|
pub color: [f32; 4],
|
||||||
|
|
||||||
|
/// The [`Quad`] data of the [`Solid`].
|
||||||
|
pub quad: Quad,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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 [`Quad`] data of the [`Gradient`].
|
||||||
|
pub quad: Quad,
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
#[allow(unsafe_code)]
|
||||||
unsafe impl bytemuck::Pod for Quad {}
|
unsafe impl Pod for Gradient {}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
unsafe impl Zeroable for Gradient {}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ mod quad;
|
||||||
mod text;
|
mod text;
|
||||||
mod triangle;
|
mod triangle;
|
||||||
|
|
||||||
|
use buffer::Buffer;
|
||||||
|
|
||||||
pub use iced_graphics as graphics;
|
pub use iced_graphics as graphics;
|
||||||
pub use iced_graphics::core;
|
pub use iced_graphics::core;
|
||||||
|
|
||||||
|
|
@ -59,8 +61,6 @@ pub use backend::Backend;
|
||||||
pub use layer::Layer;
|
pub use layer::Layer;
|
||||||
pub use settings::Settings;
|
pub use settings::Settings;
|
||||||
|
|
||||||
use buffer::Buffer;
|
|
||||||
|
|
||||||
#[cfg(any(feature = "image", feature = "svg"))]
|
#[cfg(any(feature = "image", feature = "svg"))]
|
||||||
mod image;
|
mod image;
|
||||||
|
|
||||||
|
|
|
||||||
485
wgpu/src/quad.rs
485
wgpu/src/quad.rs
|
|
@ -1,18 +1,19 @@
|
||||||
use crate::core::Rectangle;
|
use crate::core::Rectangle;
|
||||||
use crate::graphics::Transformation;
|
use crate::graphics::Transformation;
|
||||||
use crate::layer;
|
use crate::layer;
|
||||||
use crate::Buffer;
|
|
||||||
|
|
||||||
use bytemuck::{Pod, Zeroable};
|
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use wgpu::util::DeviceExt;
|
use wgpu::util::DeviceExt;
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
use tracing::info_span;
|
use tracing::info_span;
|
||||||
|
|
||||||
|
const INITIAL_INSTANCES: usize = 2_000;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
pipeline: wgpu::RenderPipeline,
|
solid: solid::Pipeline,
|
||||||
|
gradient: gradient::Pipeline,
|
||||||
constant_layout: wgpu::BindGroupLayout,
|
constant_layout: wgpu::BindGroupLayout,
|
||||||
vertices: wgpu::Buffer,
|
vertices: wgpu::Buffer,
|
||||||
indices: wgpu::Buffer,
|
indices: wgpu::Buffer,
|
||||||
|
|
@ -39,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::<Vertex>() 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::<layer::Quad>() 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 =
|
let vertices =
|
||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("iced_wgpu::quad vertex buffer"),
|
label: Some("iced_wgpu::quad vertex buffer"),
|
||||||
contents: bytemuck::cast_slice(&QUAD_VERTS),
|
contents: bytemuck::cast_slice(&VERTICES),
|
||||||
usage: wgpu::BufferUsages::VERTEX,
|
usage: wgpu::BufferUsages::VERTEX,
|
||||||
});
|
});
|
||||||
|
|
||||||
let indices =
|
let indices =
|
||||||
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
|
||||||
label: Some("iced_wgpu::quad index buffer"),
|
label: Some("iced_wgpu::quad index buffer"),
|
||||||
contents: bytemuck::cast_slice(&QUAD_INDICES),
|
contents: bytemuck::cast_slice(&INDICES),
|
||||||
usage: wgpu::BufferUsages::INDEX,
|
usage: wgpu::BufferUsages::INDEX,
|
||||||
});
|
});
|
||||||
|
|
||||||
Pipeline {
|
Self {
|
||||||
pipeline,
|
|
||||||
constant_layout,
|
|
||||||
vertices,
|
vertices,
|
||||||
indices,
|
indices,
|
||||||
|
solid: solid::Pipeline::new(device, format, &constant_layout),
|
||||||
|
gradient: gradient::Pipeline::new(device, format, &constant_layout),
|
||||||
layers: Vec::new(),
|
layers: Vec::new(),
|
||||||
prepare_layer: 0,
|
prepare_layer: 0,
|
||||||
|
constant_layout,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -147,7 +69,7 @@ impl Pipeline {
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
instances: &[layer::Quad],
|
instances: &layer::Quads,
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
) {
|
) {
|
||||||
|
|
@ -168,22 +90,27 @@ impl Pipeline {
|
||||||
render_pass: &mut wgpu::RenderPass<'a>,
|
render_pass: &mut wgpu::RenderPass<'a>,
|
||||||
) {
|
) {
|
||||||
if let Some(layer) = self.layers.get(layer) {
|
if let Some(layer) = self.layers.get(layer) {
|
||||||
render_pass.set_pipeline(&self.pipeline);
|
|
||||||
|
|
||||||
render_pass.set_scissor_rect(
|
render_pass.set_scissor_rect(
|
||||||
bounds.x,
|
bounds.x,
|
||||||
bounds.y,
|
bounds.y,
|
||||||
bounds.width,
|
bounds.width,
|
||||||
bounds.height,
|
bounds.height,
|
||||||
);
|
);
|
||||||
|
|
||||||
render_pass.set_index_buffer(
|
render_pass.set_index_buffer(
|
||||||
self.indices.slice(..),
|
self.indices.slice(..),
|
||||||
wgpu::IndexFormat::Uint16,
|
wgpu::IndexFormat::Uint16,
|
||||||
);
|
);
|
||||||
render_pass.set_vertex_buffer(0, self.vertices.slice(..));
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -196,8 +123,8 @@ impl Pipeline {
|
||||||
struct Layer {
|
struct Layer {
|
||||||
constants: wgpu::BindGroup,
|
constants: wgpu::BindGroup,
|
||||||
constants_buffer: wgpu::Buffer,
|
constants_buffer: wgpu::Buffer,
|
||||||
instances: Buffer<layer::Quad>,
|
solid: solid::Layer,
|
||||||
instance_count: usize,
|
gradient: gradient::Layer,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Layer {
|
impl Layer {
|
||||||
|
|
@ -221,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 {
|
Self {
|
||||||
constants,
|
constants,
|
||||||
constants_buffer,
|
constants_buffer,
|
||||||
instances,
|
solid: solid::Layer::new(device),
|
||||||
instance_count: 0,
|
gradient: gradient::Layer::new(device),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -240,7 +160,7 @@ impl Layer {
|
||||||
&mut self,
|
&mut self,
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
instances: &[layer::Quad],
|
instances: &layer::Quads,
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
scale: f32,
|
scale: f32,
|
||||||
) {
|
) {
|
||||||
|
|
@ -255,35 +175,350 @@ impl Layer {
|
||||||
bytemuck::bytes_of(&uniforms),
|
bytemuck::bytes_of(&uniforms),
|
||||||
);
|
);
|
||||||
|
|
||||||
let _ = self.instances.resize(device, instances.len());
|
let _ = self.solid.instances.resize(device, instances.solids.len());
|
||||||
self.instances.write(queue, 0, instances);
|
let _ = self
|
||||||
self.instance_count = instances.len();
|
.gradient
|
||||||
}
|
.instances
|
||||||
|
.resize(device, instances.gradients.len());
|
||||||
pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
|
let _ =
|
||||||
#[cfg(feature = "tracing")]
|
self.solid
|
||||||
let _ = info_span!("Wgpu::Quad", "DRAW").entered();
|
.instances
|
||||||
|
.write(queue, 0, instances.solids.as_slice());
|
||||||
render_pass.set_bind_group(0, &self.constants, &[]);
|
self.solid.instance_count = instances.solids.len();
|
||||||
render_pass.set_vertex_buffer(1, self.instances.slice(..));
|
let _ = self.gradient.instances.write(
|
||||||
|
queue,
|
||||||
render_pass.draw_indexed(
|
|
||||||
0..QUAD_INDICES.len() as u32,
|
|
||||||
0,
|
0,
|
||||||
0..self.instance_count as u32,
|
instances.gradients.as_slice(),
|
||||||
);
|
);
|
||||||
|
self.gradient.instance_count = instances.gradients.len();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod solid {
|
||||||
|
use crate::layer::quad;
|
||||||
|
use crate::quad::{color_target_state, Vertex, INDICES, INITIAL_INSTANCES};
|
||||||
|
use crate::Buffer;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pipeline {
|
||||||
|
pub pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Layer {
|
||||||
|
pub instances: Buffer<quad::Solid>,
|
||||||
|
pub instance_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layer {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let instances = Buffer::new(
|
||||||
|
device,
|
||||||
|
"iced_wgpu.quad.solid.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 _ = tracing::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"),
|
||||||
|
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: std::mem::size_of::<quad::Solid>()
|
||||||
|
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::layer::quad;
|
||||||
|
use crate::quad::{color_target_state, Vertex, INDICES, INITIAL_INSTANCES};
|
||||||
|
use crate::Buffer;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Pipeline {
|
||||||
|
pub pipeline: wgpu::RenderPipeline,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Layer {
|
||||||
|
pub instances: Buffer<quad::Gradient>,
|
||||||
|
pub instance_count: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Layer {
|
||||||
|
pub fn new(device: &wgpu::Device) -> Self {
|
||||||
|
let instances = Buffer::new(
|
||||||
|
device,
|
||||||
|
"iced_wgpu.quad.gradient.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 _ =
|
||||||
|
tracing::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"),
|
||||||
|
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: std::mem::size_of::<
|
||||||
|
quad::Gradient,
|
||||||
|
>(
|
||||||
|
)
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn color_target_state(
|
||||||
|
format: wgpu::TextureFormat,
|
||||||
|
) -> [Option<wgpu::ColorTargetState>; 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)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, Zeroable, Pod)]
|
#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
|
||||||
pub struct Vertex {
|
pub struct Vertex {
|
||||||
_position: [f32; 2],
|
_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::<Self>() as u64,
|
||||||
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
|
attributes: &[wgpu::VertexAttribute {
|
||||||
|
shader_location: 0,
|
||||||
|
format: wgpu::VertexFormat::Float32x2,
|
||||||
|
offset: 0,
|
||||||
|
}],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const QUAD_VERTS: [Vertex; 4] = [
|
const INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3];
|
||||||
|
|
||||||
|
const VERTICES: [Vertex; 4] = [
|
||||||
Vertex {
|
Vertex {
|
||||||
_position: [0.0, 0.0],
|
_position: [0.0, 0.0],
|
||||||
},
|
},
|
||||||
|
|
@ -298,10 +533,8 @@ const QUAD_VERTS: [Vertex; 4] = [
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const INITIAL_INSTANCES: usize = 10_000;
|
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Debug, Clone, Copy, Zeroable, Pod)]
|
#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)]
|
||||||
struct Uniforms {
|
struct Uniforms {
|
||||||
transform: [f32; 16],
|
transform: [f32; 16],
|
||||||
scale: f32,
|
scale: f32,
|
||||||
|
|
|
||||||
|
|
@ -1,88 +0,0 @@
|
||||||
struct Uniforms {
|
|
||||||
transform: mat4x4<f32>,
|
|
||||||
//xy = start, wz = end
|
|
||||||
position: vec4<f32>,
|
|
||||||
//x = start stop, y = end stop, zw = padding
|
|
||||||
stop_range: vec4<i32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Stop {
|
|
||||||
color: vec4<f32>,
|
|
||||||
offset: f32,
|
|
||||||
};
|
|
||||||
|
|
||||||
@group(0) @binding(0)
|
|
||||||
var<uniform> uniforms: Uniforms;
|
|
||||||
|
|
||||||
@group(0) @binding(1)
|
|
||||||
var<storage, read> color_stops: array<Stop>;
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) raw_position: vec2<f32>
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(@location(0) input: vec2<f32>) -> VertexOutput {
|
|
||||||
var output: VertexOutput;
|
|
||||||
output.position = uniforms.transform * vec4<f32>(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<f32> {
|
|
||||||
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<f32>;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
@ -5,59 +5,6 @@ struct Globals {
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> globals: Globals;
|
@group(0) @binding(0) var<uniform> globals: Globals;
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) v_pos: vec2<f32>,
|
|
||||||
@location(1) pos: vec2<f32>,
|
|
||||||
@location(2) scale: vec2<f32>,
|
|
||||||
@location(3) color: vec4<f32>,
|
|
||||||
@location(4) border_color: vec4<f32>,
|
|
||||||
@location(5) border_radius: vec4<f32>,
|
|
||||||
@location(6) border_width: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) color: vec4<f32>,
|
|
||||||
@location(1) border_color: vec4<f32>,
|
|
||||||
@location(2) pos: vec2<f32>,
|
|
||||||
@location(3) scale: vec2<f32>,
|
|
||||||
@location(4) border_radius: vec4<f32>,
|
|
||||||
@location(5) border_width: f32,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(input: VertexInput) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
|
|
||||||
var pos: vec2<f32> = input.pos * globals.scale;
|
|
||||||
var scale: vec2<f32> = input.scale * globals.scale;
|
|
||||||
|
|
||||||
var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
|
|
||||||
var border_radius: vec4<f32> = vec4<f32>(
|
|
||||||
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<f32> = mat4x4<f32>(
|
|
||||||
vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
|
|
||||||
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
||||||
vec4<f32>(pos - vec2<f32>(0.5, 0.5), 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<f32>(input.v_pos, 0.0, 1.0);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn distance_alg(
|
fn distance_alg(
|
||||||
frag_coord: vec2<f32>,
|
frag_coord: vec2<f32>,
|
||||||
position: vec2<f32>,
|
position: vec2<f32>,
|
||||||
|
|
@ -91,10 +38,62 @@ fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>)
|
||||||
return rx;
|
return rx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SolidVertexInput {
|
||||||
|
@location(0) v_pos: vec2<f32>,
|
||||||
|
@location(1) color: vec4<f32>,
|
||||||
|
@location(2) pos: vec2<f32>,
|
||||||
|
@location(3) scale: vec2<f32>,
|
||||||
|
@location(4) border_color: vec4<f32>,
|
||||||
|
@location(5) border_radius: vec4<f32>,
|
||||||
|
@location(6) border_width: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SolidVertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) color: vec4<f32>,
|
||||||
|
@location(1) border_color: vec4<f32>,
|
||||||
|
@location(2) pos: vec2<f32>,
|
||||||
|
@location(3) scale: vec2<f32>,
|
||||||
|
@location(4) border_radius: vec4<f32>,
|
||||||
|
@location(5) border_width: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
|
||||||
|
var out: SolidVertexOutput;
|
||||||
|
|
||||||
|
var pos: vec2<f32> = input.pos * globals.scale;
|
||||||
|
var scale: vec2<f32> = input.scale * globals.scale;
|
||||||
|
|
||||||
|
var min_border_radius = min(input.scale.x, input.scale.y) * 0.5;
|
||||||
|
var border_radius: vec4<f32> = vec4<f32>(
|
||||||
|
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<f32> = mat4x4<f32>(
|
||||||
|
vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
|
||||||
|
vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
|
||||||
|
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
||||||
|
vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
out.position = globals.transform * transform * vec4<f32>(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;
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
@fragment
|
@fragment
|
||||||
fn fs_main(
|
fn solid_fs_main(
|
||||||
input: VertexOutput
|
input: SolidVertexOutput
|
||||||
) -> @location(0) vec4<f32> {
|
) -> @location(0) vec4<f32> {
|
||||||
var mixed_color: vec4<f32> = input.color;
|
var mixed_color: vec4<f32> = input.color;
|
||||||
|
|
||||||
|
|
@ -138,3 +137,214 @@ fn fs_main(
|
||||||
|
|
||||||
return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
|
return vec4<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct GradientVertexInput {
|
||||||
|
@location(0) v_pos: vec2<f32>,
|
||||||
|
@location(1) color_1: vec4<f32>,
|
||||||
|
@location(2) color_2: vec4<f32>,
|
||||||
|
@location(3) color_3: vec4<f32>,
|
||||||
|
@location(4) color_4: vec4<f32>,
|
||||||
|
@location(5) color_5: vec4<f32>,
|
||||||
|
@location(6) color_6: vec4<f32>,
|
||||||
|
@location(7) color_7: vec4<f32>,
|
||||||
|
@location(8) color_8: vec4<f32>,
|
||||||
|
@location(9) offsets_1: vec4<f32>,
|
||||||
|
@location(10) offsets_2: vec4<f32>,
|
||||||
|
@location(11) direction: vec4<f32>,
|
||||||
|
@location(12) position_and_scale: vec4<f32>,
|
||||||
|
@location(13) border_color: vec4<f32>,
|
||||||
|
@location(14) border_radius: vec4<f32>,
|
||||||
|
@location(15) border_width: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GradientVertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(1) color_1: vec4<f32>,
|
||||||
|
@location(2) color_2: vec4<f32>,
|
||||||
|
@location(3) color_3: vec4<f32>,
|
||||||
|
@location(4) color_4: vec4<f32>,
|
||||||
|
@location(5) color_5: vec4<f32>,
|
||||||
|
@location(6) color_6: vec4<f32>,
|
||||||
|
@location(7) color_7: vec4<f32>,
|
||||||
|
@location(8) color_8: vec4<f32>,
|
||||||
|
@location(9) offsets_1: vec4<f32>,
|
||||||
|
@location(10) offsets_2: vec4<f32>,
|
||||||
|
@location(11) direction: vec4<f32>,
|
||||||
|
@location(12) position_and_scale: vec4<f32>,
|
||||||
|
@location(13) border_color: vec4<f32>,
|
||||||
|
@location(14) border_radius: vec4<f32>,
|
||||||
|
@location(15) border_width: f32
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
|
||||||
|
var out: GradientVertexOutput;
|
||||||
|
|
||||||
|
var pos: vec2<f32> = input.position_and_scale.xy * globals.scale;
|
||||||
|
var scale: vec2<f32> = 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<f32> = vec4<f32>(
|
||||||
|
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<f32> = mat4x4<f32>(
|
||||||
|
vec4<f32>(scale.x + 1.0, 0.0, 0.0, 0.0),
|
||||||
|
vec4<f32>(0.0, scale.y + 1.0, 0.0, 0.0),
|
||||||
|
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
||||||
|
vec4<f32>(pos - vec2<f32>(0.5, 0.5), 0.0, 1.0)
|
||||||
|
);
|
||||||
|
|
||||||
|
out.position = globals.transform * transform * vec4<f32>(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<f32>(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;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random(coords: vec2<f32>) -> 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<f32>,
|
||||||
|
direction: vec4<f32>,
|
||||||
|
colors: array<vec4<f32>, 8>,
|
||||||
|
offsets: array<f32, 8>,
|
||||||
|
last_index: i32
|
||||||
|
) -> vec4<f32> {
|
||||||
|
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<f32>;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
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 + mix(-noise_granularity, noise_granularity, random(raw_position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let colors = array<vec4<f32>, 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<f32, 8>(
|
||||||
|
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<f32> = 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<f32>(input.border_width, input.border_width),
|
||||||
|
scale - vec2<f32>(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<f32>(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<f32>(mixed_color.x, mixed_color.y, mixed_color.z, mixed_color.w * radius_alpha);
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,30 +0,0 @@
|
||||||
struct Globals {
|
|
||||||
transform: mat4x4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@group(0) @binding(0) var<uniform> globals: Globals;
|
|
||||||
|
|
||||||
struct VertexInput {
|
|
||||||
@location(0) position: vec2<f32>,
|
|
||||||
@location(1) color: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct VertexOutput {
|
|
||||||
@builtin(position) position: vec4<f32>,
|
|
||||||
@location(0) color: vec4<f32>,
|
|
||||||
}
|
|
||||||
|
|
||||||
@vertex
|
|
||||||
fn vs_main(input: VertexInput) -> VertexOutput {
|
|
||||||
var out: VertexOutput;
|
|
||||||
|
|
||||||
out.color = input.color;
|
|
||||||
out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@fragment
|
|
||||||
fn fs_main(input: VertexOutput) -> @location(0) vec4<f32> {
|
|
||||||
return input.color;
|
|
||||||
}
|
|
||||||
168
wgpu/src/shader/triangle.wgsl
Normal file
168
wgpu/src/shader/triangle.wgsl
Normal file
|
|
@ -0,0 +1,168 @@
|
||||||
|
struct Globals {
|
||||||
|
transform: mat4x4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@group(0) @binding(0) var<uniform> globals: Globals;
|
||||||
|
|
||||||
|
struct SolidVertexInput {
|
||||||
|
@location(0) position: vec2<f32>,
|
||||||
|
@location(1) color: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SolidVertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) color: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn solid_vs_main(input: SolidVertexInput) -> SolidVertexOutput {
|
||||||
|
var out: SolidVertexOutput;
|
||||||
|
|
||||||
|
out.color = input.color;
|
||||||
|
out.position = globals.transform * vec4<f32>(input.position, 0.0, 1.0);
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
return input.color;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct GradientVertexOutput {
|
||||||
|
@builtin(position) position: vec4<f32>,
|
||||||
|
@location(0) raw_position: vec2<f32>,
|
||||||
|
@location(1) color_1: vec4<f32>,
|
||||||
|
@location(2) color_2: vec4<f32>,
|
||||||
|
@location(3) color_3: vec4<f32>,
|
||||||
|
@location(4) color_4: vec4<f32>,
|
||||||
|
@location(5) color_5: vec4<f32>,
|
||||||
|
@location(6) color_6: vec4<f32>,
|
||||||
|
@location(7) color_7: vec4<f32>,
|
||||||
|
@location(8) color_8: vec4<f32>,
|
||||||
|
@location(9) offsets_1: vec4<f32>,
|
||||||
|
@location(10) offsets_2: vec4<f32>,
|
||||||
|
@location(11) direction: vec4<f32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
@vertex
|
||||||
|
fn gradient_vs_main(
|
||||||
|
@location(0) input: vec2<f32>,
|
||||||
|
@location(1) color_1: vec4<f32>,
|
||||||
|
@location(2) color_2: vec4<f32>,
|
||||||
|
@location(3) color_3: vec4<f32>,
|
||||||
|
@location(4) color_4: vec4<f32>,
|
||||||
|
@location(5) color_5: vec4<f32>,
|
||||||
|
@location(6) color_6: vec4<f32>,
|
||||||
|
@location(7) color_7: vec4<f32>,
|
||||||
|
@location(8) color_8: vec4<f32>,
|
||||||
|
@location(9) offsets_1: vec4<f32>,
|
||||||
|
@location(10) offsets_2: vec4<f32>,
|
||||||
|
@location(11) direction: vec4<f32>,
|
||||||
|
) -> GradientVertexOutput {
|
||||||
|
var output: GradientVertexOutput;
|
||||||
|
|
||||||
|
output.position = globals.transform * vec4<f32>(input.xy, 0.0, 1.0);
|
||||||
|
output.raw_position = input;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn random(coords: vec2<f32>) -> 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<f32>,
|
||||||
|
direction: vec4<f32>,
|
||||||
|
colors: array<vec4<f32>, 8>,
|
||||||
|
offsets: array<f32, 8>,
|
||||||
|
last_index: i32
|
||||||
|
) -> vec4<f32> {
|
||||||
|
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<f32>;
|
||||||
|
|
||||||
|
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];
|
||||||
|
|
||||||
|
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 + mix(-noise_granularity, noise_granularity, random(raw_position));
|
||||||
|
}
|
||||||
|
|
||||||
|
@fragment
|
||||||
|
fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
|
||||||
|
let colors = array<vec4<f32>, 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<f32, 8>(
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,19 @@
|
||||||
//! Draw meshes of triangles.
|
//! Draw meshes of triangles.
|
||||||
mod msaa;
|
mod msaa;
|
||||||
|
|
||||||
use crate::buffer::r#static::Buffer;
|
|
||||||
use crate::core::Size;
|
use crate::core::Size;
|
||||||
use crate::graphics::{Antialiasing, Transformation};
|
use crate::graphics::{Antialiasing, Transformation};
|
||||||
use crate::layer::mesh::{self, Mesh};
|
use crate::layer::mesh::{self, Mesh};
|
||||||
|
use crate::Buffer;
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
const INITIAL_INDEX_COUNT: usize = 1_000;
|
||||||
use crate::core::Gradient;
|
const INITIAL_VERTEX_COUNT: usize = 1_000;
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
|
||||||
use tracing::info_span;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
blit: Option<msaa::Blit>,
|
blit: Option<msaa::Blit>,
|
||||||
solid: solid::Pipeline,
|
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,
|
gradient: gradient::Pipeline,
|
||||||
|
|
||||||
layers: Vec<Layer>,
|
layers: Vec<Layer>,
|
||||||
prepare_layer: usize,
|
prepare_layer: usize,
|
||||||
}
|
}
|
||||||
|
|
@ -30,8 +23,6 @@ struct Layer {
|
||||||
index_buffer: Buffer<u32>,
|
index_buffer: Buffer<u32>,
|
||||||
index_strides: Vec<u32>,
|
index_strides: Vec<u32>,
|
||||||
solid: solid::Layer,
|
solid: solid::Layer,
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
gradient: gradient::Layer,
|
gradient: gradient::Layer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,18 +30,17 @@ impl Layer {
|
||||||
fn new(
|
fn new(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
solid: &solid::Pipeline,
|
solid: &solid::Pipeline,
|
||||||
#[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline,
|
gradient: &gradient::Pipeline,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
index_buffer: Buffer::new(
|
index_buffer: Buffer::new(
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle index buffer",
|
"iced_wgpu.triangle.index_buffer",
|
||||||
|
INITIAL_INDEX_COUNT,
|
||||||
wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
|
||||||
),
|
),
|
||||||
index_strides: Vec::new(),
|
index_strides: Vec::new(),
|
||||||
solid: solid::Layer::new(device, &solid.constants_layout),
|
solid: solid::Layer::new(device, &solid.constants_layout),
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
gradient: gradient::Layer::new(device, &gradient.constants_layout),
|
gradient: gradient::Layer::new(device, &gradient.constants_layout),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +50,7 @@ impl Layer {
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
queue: &wgpu::Queue,
|
queue: &wgpu::Queue,
|
||||||
solid: &solid::Pipeline,
|
solid: &solid::Pipeline,
|
||||||
#[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline,
|
gradient: &gradient::Pipeline,
|
||||||
meshes: &[Mesh<'_>],
|
meshes: &[Mesh<'_>],
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
) {
|
) {
|
||||||
|
|
@ -73,177 +63,92 @@ impl Layer {
|
||||||
// the majority of use cases. Therefore we will write GPU data every frame (for now).
|
// 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.index_buffer.resize(device, count.indices);
|
||||||
let _ = self.solid.vertices.resize(device, count.solid_vertices);
|
let _ = self.solid.vertices.resize(device, count.solid_vertices);
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let _ = self
|
let _ = self
|
||||||
.gradient
|
.gradient
|
||||||
.vertices
|
.vertices
|
||||||
.resize(device, count.gradient_vertices);
|
.resize(device, count.gradient_vertices);
|
||||||
|
|
||||||
// Prepare dynamic buffers & data store for writing
|
if self.solid.uniforms.resize(device, count.solids) {
|
||||||
self.index_buffer.clear();
|
self.solid.constants = solid::Layer::bind_group(
|
||||||
self.index_strides.clear();
|
device,
|
||||||
self.solid.vertices.clear();
|
&self.solid.uniforms.raw,
|
||||||
self.solid.uniforms.clear();
|
&solid.constants_layout,
|
||||||
|
);
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
{
|
|
||||||
self.gradient.uniforms.clear();
|
|
||||||
self.gradient.vertices.clear();
|
|
||||||
self.gradient.storage.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut solid_vertex_offset = 0;
|
if self.gradient.uniforms.resize(device, count.gradients) {
|
||||||
let mut index_offset = 0;
|
self.gradient.constants = gradient::Layer::bind_group(
|
||||||
|
device,
|
||||||
|
&self.gradient.uniforms.raw,
|
||||||
|
&gradient.constants_layout,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
self.index_strides.clear();
|
||||||
|
self.index_buffer.clear();
|
||||||
|
self.solid.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_vertex_offset = 0;
|
||||||
|
let mut gradient_uniform_offset = 0;
|
||||||
|
let mut index_offset = 0;
|
||||||
|
|
||||||
for mesh in meshes {
|
for mesh in meshes {
|
||||||
let origin = mesh.origin();
|
let origin = mesh.origin();
|
||||||
let indices = mesh.indices();
|
let indices = mesh.indices();
|
||||||
|
|
||||||
let transform =
|
let uniforms = Uniforms::new(
|
||||||
transformation * Transformation::translate(origin.x, origin.y);
|
transformation * Transformation::translate(origin.x, origin.y),
|
||||||
|
);
|
||||||
|
|
||||||
let new_index_offset =
|
index_offset +=
|
||||||
self.index_buffer.write(queue, index_offset, indices);
|
self.index_buffer.write(queue, index_offset, indices);
|
||||||
|
|
||||||
index_offset += new_index_offset;
|
|
||||||
self.index_strides.push(indices.len() as u32);
|
self.index_strides.push(indices.len() as u32);
|
||||||
|
|
||||||
//push uniform data to CPU buffers
|
|
||||||
match mesh {
|
match mesh {
|
||||||
Mesh::Solid { buffers, .. } => {
|
Mesh::Solid { buffers, .. } => {
|
||||||
self.solid.uniforms.push(&solid::Uniforms::new(transform));
|
solid_vertex_offset += self.solid.vertices.write(
|
||||||
|
|
||||||
let written_bytes = self.solid.vertices.write(
|
|
||||||
queue,
|
queue,
|
||||||
solid_vertex_offset,
|
solid_vertex_offset,
|
||||||
&buffers.vertices,
|
&buffers.vertices,
|
||||||
);
|
);
|
||||||
|
|
||||||
solid_vertex_offset += written_bytes;
|
solid_uniform_offset += self.solid.uniforms.write(
|
||||||
|
queue,
|
||||||
|
solid_uniform_offset,
|
||||||
|
&[uniforms],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
Mesh::Gradient { buffers, .. } => {
|
||||||
Mesh::Gradient {
|
gradient_vertex_offset += self.gradient.vertices.write(
|
||||||
buffers, gradient, ..
|
|
||||||
} => {
|
|
||||||
let written_bytes = self.gradient.vertices.write(
|
|
||||||
queue,
|
queue,
|
||||||
gradient_vertex_offset,
|
gradient_vertex_offset,
|
||||||
&buffers.vertices,
|
&buffers.vertices,
|
||||||
);
|
);
|
||||||
|
|
||||||
gradient_vertex_offset += written_bytes;
|
gradient_uniform_offset += self.gradient.uniforms.write(
|
||||||
|
queue,
|
||||||
match gradient {
|
gradient_uniform_offset,
|
||||||
Gradient::Linear(linear) => {
|
&[uniforms],
|
||||||
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<gradient::ColorStop> = 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
Mesh::Gradient { .. } => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 {
|
|
||||||
self.gradient.constants = gradient::Layer::bind_group(
|
|
||||||
device,
|
|
||||||
self.gradient.uniforms.raw(),
|
|
||||||
self.gradient.storage.raw(),
|
|
||||||
&gradient.constants_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>(
|
fn render<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
solid: &'a solid::Pipeline,
|
solid: &'a solid::Pipeline,
|
||||||
#[cfg(not(target_arch = "wasm32"))] gradient: &'a gradient::Pipeline,
|
gradient: &'a gradient::Pipeline,
|
||||||
meshes: &[Mesh<'_>],
|
meshes: &[Mesh<'_>],
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
render_pass: &mut wgpu::RenderPass<'a>,
|
render_pass: &mut wgpu::RenderPass<'a>,
|
||||||
) {
|
) {
|
||||||
let mut num_solids = 0;
|
let mut num_solids = 0;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
let mut num_gradients = 0;
|
let mut num_gradients = 0;
|
||||||
let mut last_is_solid = None;
|
let mut last_is_solid = None;
|
||||||
|
|
||||||
|
|
@ -268,7 +173,8 @@ impl Layer {
|
||||||
render_pass.set_bind_group(
|
render_pass.set_bind_group(
|
||||||
0,
|
0,
|
||||||
&self.solid.constants,
|
&self.solid.constants,
|
||||||
&[self.solid.uniforms.offset_at_index(num_solids)],
|
&[(num_solids * std::mem::size_of::<Uniforms>())
|
||||||
|
as u32],
|
||||||
);
|
);
|
||||||
|
|
||||||
render_pass.set_vertex_buffer(
|
render_pass.set_vertex_buffer(
|
||||||
|
|
@ -278,7 +184,6 @@ impl Layer {
|
||||||
|
|
||||||
num_solids += 1;
|
num_solids += 1;
|
||||||
}
|
}
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
Mesh::Gradient { .. } => {
|
Mesh::Gradient { .. } => {
|
||||||
if last_is_solid.unwrap_or(true) {
|
if last_is_solid.unwrap_or(true) {
|
||||||
render_pass.set_pipeline(&gradient.pipeline);
|
render_pass.set_pipeline(&gradient.pipeline);
|
||||||
|
|
@ -289,10 +194,8 @@ impl Layer {
|
||||||
render_pass.set_bind_group(
|
render_pass.set_bind_group(
|
||||||
0,
|
0,
|
||||||
&self.gradient.constants,
|
&self.gradient.constants,
|
||||||
&[self
|
&[(num_gradients * std::mem::size_of::<Uniforms>())
|
||||||
.gradient
|
as u32],
|
||||||
.uniforms
|
|
||||||
.offset_at_index(num_gradients)],
|
|
||||||
);
|
);
|
||||||
|
|
||||||
render_pass.set_vertex_buffer(
|
render_pass.set_vertex_buffer(
|
||||||
|
|
@ -302,8 +205,6 @@ impl Layer {
|
||||||
|
|
||||||
num_gradients += 1;
|
num_gradients += 1;
|
||||||
}
|
}
|
||||||
#[cfg(target_arch = "wasm32")]
|
|
||||||
Mesh::Gradient { .. } => {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
render_pass.set_index_buffer(
|
render_pass.set_index_buffer(
|
||||||
|
|
@ -325,10 +226,7 @@ impl Pipeline {
|
||||||
Pipeline {
|
Pipeline {
|
||||||
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
|
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
|
||||||
solid: solid::Pipeline::new(device, format, antialiasing),
|
solid: solid::Pipeline::new(device, format, antialiasing),
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
gradient: gradient::Pipeline::new(device, format, antialiasing),
|
gradient: gradient::Pipeline::new(device, format, antialiasing),
|
||||||
|
|
||||||
layers: Vec::new(),
|
layers: Vec::new(),
|
||||||
prepare_layer: 0,
|
prepare_layer: 0,
|
||||||
}
|
}
|
||||||
|
|
@ -342,15 +240,11 @@ impl Pipeline {
|
||||||
transformation: Transformation,
|
transformation: Transformation,
|
||||||
) {
|
) {
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _ = info_span!("Wgpu::Triangle", "PREPARE").entered();
|
let _ = tracing::info_span!("Wgpu::Triangle", "PREPARE").entered();
|
||||||
|
|
||||||
if self.layers.len() <= self.prepare_layer {
|
if self.layers.len() <= self.prepare_layer {
|
||||||
self.layers.push(Layer::new(
|
self.layers
|
||||||
device,
|
.push(Layer::new(device, &self.solid, &self.gradient));
|
||||||
&self.solid,
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
&self.gradient,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let layer = &mut self.layers[self.prepare_layer];
|
let layer = &mut self.layers[self.prepare_layer];
|
||||||
|
|
@ -358,7 +252,6 @@ impl Pipeline {
|
||||||
device,
|
device,
|
||||||
queue,
|
queue,
|
||||||
&self.solid,
|
&self.solid,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
&self.gradient,
|
&self.gradient,
|
||||||
meshes,
|
meshes,
|
||||||
transformation,
|
transformation,
|
||||||
|
|
@ -378,9 +271,8 @@ impl Pipeline {
|
||||||
scale_factor: f32,
|
scale_factor: f32,
|
||||||
) {
|
) {
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
let _ = info_span!("Wgpu::Triangle", "DRAW").entered();
|
let _ = tracing::info_span!("Wgpu::Triangle", "DRAW").entered();
|
||||||
|
|
||||||
// Configure render pass
|
|
||||||
{
|
{
|
||||||
let (attachment, resolve_target, load) = if let Some(blit) =
|
let (attachment, resolve_target, load) = if let Some(blit) =
|
||||||
&mut self.blit
|
&mut self.blit
|
||||||
|
|
@ -397,12 +289,9 @@ impl Pipeline {
|
||||||
(target, None, wgpu::LoadOp::Load)
|
(target, None, wgpu::LoadOp::Load)
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "tracing")]
|
|
||||||
let _ = info_span!("Wgpu::Triangle", "BEGIN_RENDER_PASS").enter();
|
|
||||||
|
|
||||||
let mut render_pass =
|
let mut render_pass =
|
||||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||||
label: Some("iced_wgpu::triangle render pass"),
|
label: Some("iced_wgpu.triangle.render_pass"),
|
||||||
color_attachments: &[Some(
|
color_attachments: &[Some(
|
||||||
wgpu::RenderPassColorAttachment {
|
wgpu::RenderPassColorAttachment {
|
||||||
view: attachment,
|
view: attachment,
|
||||||
|
|
@ -417,7 +306,6 @@ impl Pipeline {
|
||||||
|
|
||||||
layer.render(
|
layer.render(
|
||||||
&self.solid,
|
&self.solid,
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
&self.gradient,
|
&self.gradient,
|
||||||
meshes,
|
meshes,
|
||||||
scale_factor,
|
scale_factor,
|
||||||
|
|
@ -463,14 +351,48 @@ fn multisample_state(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod solid {
|
#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
|
||||||
use crate::buffer::dynamic;
|
#[repr(C)]
|
||||||
use crate::buffer::r#static::Buffer;
|
pub struct Uniforms {
|
||||||
use crate::graphics::primitive;
|
transform: [f32; 16],
|
||||||
use crate::graphics::{Antialiasing, Transformation};
|
/// Uniform values must be 256-aligned;
|
||||||
use crate::triangle;
|
/// see: [`wgpu::Limits`] `min_uniform_buffer_offset_alignment`.
|
||||||
|
_padding: [f32; 48],
|
||||||
|
}
|
||||||
|
|
||||||
use encase::ShaderType;
|
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(
|
||||||
|
std::mem::size_of::<Self>() as u64,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
count: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn min_size() -> Option<wgpu::BufferSize> {
|
||||||
|
wgpu::BufferSize::new(std::mem::size_of::<Self>() as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod solid {
|
||||||
|
use crate::graphics::primitive;
|
||||||
|
use crate::graphics::Antialiasing;
|
||||||
|
use crate::triangle;
|
||||||
|
use crate::Buffer;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
|
|
@ -481,7 +403,7 @@ mod solid {
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Layer {
|
pub struct Layer {
|
||||||
pub vertices: Buffer<primitive::ColoredVertex2D>,
|
pub vertices: Buffer<primitive::ColoredVertex2D>,
|
||||||
pub uniforms: dynamic::Buffer<Uniforms>,
|
pub uniforms: Buffer<triangle::Uniforms>,
|
||||||
pub constants: wgpu::BindGroup,
|
pub constants: wgpu::BindGroup,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -492,17 +414,20 @@ mod solid {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let vertices = Buffer::new(
|
let vertices = Buffer::new(
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle::solid vertex buffer",
|
"iced_wgpu.triangle.solid.vertex_buffer",
|
||||||
|
triangle::INITIAL_VERTEX_COUNT,
|
||||||
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
);
|
);
|
||||||
|
|
||||||
let uniforms = dynamic::Buffer::uniform(
|
let uniforms = Buffer::new(
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle::solid uniforms",
|
"iced_wgpu.triangle.solid.uniforms",
|
||||||
|
1,
|
||||||
|
wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
);
|
);
|
||||||
|
|
||||||
let constants =
|
let constants =
|
||||||
Self::bind_group(device, uniforms.raw(), constants_layout);
|
Self::bind_group(device, &uniforms.raw, constants_layout);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
vertices,
|
vertices,
|
||||||
|
|
@ -517,7 +442,7 @@ mod solid {
|
||||||
layout: &wgpu::BindGroupLayout,
|
layout: &wgpu::BindGroupLayout,
|
||||||
) -> wgpu::BindGroup {
|
) -> wgpu::BindGroup {
|
||||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some("iced_wgpu::triangle::solid bind group"),
|
label: Some("iced_wgpu.triangle.solid.bind_group"),
|
||||||
layout,
|
layout,
|
||||||
entries: &[wgpu::BindGroupEntry {
|
entries: &[wgpu::BindGroupEntry {
|
||||||
binding: 0,
|
binding: 0,
|
||||||
|
|
@ -525,7 +450,7 @@ mod solid {
|
||||||
wgpu::BufferBinding {
|
wgpu::BufferBinding {
|
||||||
buffer,
|
buffer,
|
||||||
offset: 0,
|
offset: 0,
|
||||||
size: Some(Uniforms::min_size()),
|
size: triangle::Uniforms::min_size(),
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
}],
|
}],
|
||||||
|
|
@ -533,21 +458,7 @@ 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 {
|
impl Pipeline {
|
||||||
/// Creates a new [SolidPipeline] using `solid.wgsl` shader.
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
format: wgpu::TextureFormat,
|
format: wgpu::TextureFormat,
|
||||||
|
|
@ -555,23 +466,14 @@ mod solid {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let constants_layout = device.create_bind_group_layout(
|
let constants_layout = device.create_bind_group_layout(
|
||||||
&wgpu::BindGroupLayoutDescriptor {
|
&wgpu::BindGroupLayoutDescriptor {
|
||||||
label: Some("iced_wgpu::triangle::solid bind group layout"),
|
label: Some("iced_wgpu.triangle.solid.bind_group_layout"),
|
||||||
entries: &[wgpu::BindGroupLayoutEntry {
|
entries: &[triangle::Uniforms::entry()],
|
||||||
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,
|
|
||||||
}],
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let layout = device.create_pipeline_layout(
|
let layout = device.create_pipeline_layout(
|
||||||
&wgpu::PipelineLayoutDescriptor {
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some("iced_wgpu::triangle::solid pipeline layout"),
|
label: Some("iced_wgpu.triangle.solid.pipeline_layout"),
|
||||||
bind_group_layouts: &[&constants_layout],
|
bind_group_layouts: &[&constants_layout],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
},
|
},
|
||||||
|
|
@ -579,12 +481,10 @@ mod solid {
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some(
|
label: Some("iced_wgpu.triangle.solid.shader"),
|
||||||
"iced_wgpu triangle solid create shader module",
|
|
||||||
),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(
|
source: wgpu::ShaderSource::Wgsl(
|
||||||
std::borrow::Cow::Borrowed(include_str!(
|
std::borrow::Cow::Borrowed(include_str!(
|
||||||
"shader/solid.wgsl"
|
"shader/triangle.wgsl"
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
@ -595,7 +495,7 @@ mod solid {
|
||||||
layout: Some(&layout),
|
layout: Some(&layout),
|
||||||
vertex: wgpu::VertexState {
|
vertex: wgpu::VertexState {
|
||||||
module: &shader,
|
module: &shader,
|
||||||
entry_point: "vs_main",
|
entry_point: "solid_vs_main",
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
buffers: &[wgpu::VertexBufferLayout {
|
||||||
array_stride: std::mem::size_of::<
|
array_stride: std::mem::size_of::<
|
||||||
primitive::ColoredVertex2D,
|
primitive::ColoredVertex2D,
|
||||||
|
|
@ -612,7 +512,7 @@ mod solid {
|
||||||
},
|
},
|
||||||
fragment: Some(wgpu::FragmentState {
|
fragment: Some(wgpu::FragmentState {
|
||||||
module: &shader,
|
module: &shader,
|
||||||
entry_point: "fs_main",
|
entry_point: "solid_fs_main",
|
||||||
targets: &[triangle::fragment_target(format)],
|
targets: &[triangle::fragment_target(format)],
|
||||||
}),
|
}),
|
||||||
primitive: triangle::primitive_state(),
|
primitive: triangle::primitive_state(),
|
||||||
|
|
@ -630,16 +530,10 @@ mod solid {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
|
||||||
mod gradient {
|
mod gradient {
|
||||||
use crate::buffer::dynamic;
|
use crate::graphics::{primitive, Antialiasing};
|
||||||
use crate::buffer::r#static::Buffer;
|
|
||||||
use crate::graphics::Antialiasing;
|
|
||||||
use crate::triangle;
|
use crate::triangle;
|
||||||
|
use crate::Buffer;
|
||||||
use encase::ShaderType;
|
|
||||||
use glam::{IVec4, Vec4};
|
|
||||||
use iced_graphics::primitive;
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Pipeline {
|
pub struct Pipeline {
|
||||||
|
|
@ -649,14 +543,9 @@ mod gradient {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Layer {
|
pub struct Layer {
|
||||||
pub vertices: Buffer<primitive::Vertex2D>,
|
pub vertices: Buffer<primitive::GradientVertex2D>,
|
||||||
pub uniforms: dynamic::Buffer<Uniforms>,
|
pub uniforms: Buffer<triangle::Uniforms>,
|
||||||
pub storage: dynamic::Buffer<Storage>,
|
|
||||||
pub constants: wgpu::BindGroup,
|
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 {
|
impl Layer {
|
||||||
|
|
@ -666,94 +555,52 @@ mod gradient {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let vertices = Buffer::new(
|
let vertices = Buffer::new(
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle::gradient vertex buffer",
|
"iced_wgpu.triangle.gradient.vertex_buffer",
|
||||||
|
triangle::INITIAL_VERTEX_COUNT,
|
||||||
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
|
||||||
);
|
);
|
||||||
|
|
||||||
let uniforms = dynamic::Buffer::uniform(
|
let uniforms = Buffer::new(
|
||||||
device,
|
device,
|
||||||
"iced_wgpu::triangle::gradient uniforms",
|
"iced_wgpu.triangle.gradient.uniforms",
|
||||||
|
1,
|
||||||
|
wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static
|
let constants =
|
||||||
// sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work
|
Self::bind_group(device, &uniforms.raw, constants_layout);
|
||||||
let storage = dynamic::Buffer::storage(
|
|
||||||
device,
|
|
||||||
"iced_wgpu::triangle::gradient storage",
|
|
||||||
);
|
|
||||||
|
|
||||||
let constants = Self::bind_group(
|
|
||||||
device,
|
|
||||||
uniforms.raw(),
|
|
||||||
storage.raw(),
|
|
||||||
constants_layout,
|
|
||||||
);
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
vertices,
|
vertices,
|
||||||
uniforms,
|
uniforms,
|
||||||
storage,
|
|
||||||
constants,
|
constants,
|
||||||
color_stop_offset: 0,
|
|
||||||
color_stops_pending_write: Storage {
|
|
||||||
color_stops: vec![],
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn bind_group(
|
pub fn bind_group(
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
uniform_buffer: &wgpu::Buffer,
|
uniform_buffer: &wgpu::Buffer,
|
||||||
storage_buffer: &wgpu::Buffer,
|
|
||||||
layout: &wgpu::BindGroupLayout,
|
layout: &wgpu::BindGroupLayout,
|
||||||
) -> wgpu::BindGroup {
|
) -> wgpu::BindGroup {
|
||||||
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
device.create_bind_group(&wgpu::BindGroupDescriptor {
|
||||||
label: Some("iced_wgpu::triangle::gradient bind group"),
|
label: Some("iced_wgpu.triangle.gradient.bind_group"),
|
||||||
layout,
|
layout,
|
||||||
entries: &[
|
entries: &[wgpu::BindGroupEntry {
|
||||||
wgpu::BindGroupEntry {
|
binding: 0,
|
||||||
binding: 0,
|
resource: wgpu::BindingResource::Buffer(
|
||||||
resource: wgpu::BindingResource::Buffer(
|
wgpu::BufferBinding {
|
||||||
wgpu::BufferBinding {
|
buffer: uniform_buffer,
|
||||||
buffer: uniform_buffer,
|
offset: 0,
|
||||||
offset: 0,
|
size: triangle::Uniforms::min_size(),
|
||||||
size: Some(Uniforms::min_size()),
|
},
|
||||||
},
|
),
|
||||||
),
|
}],
|
||||||
},
|
|
||||||
wgpu::BindGroupEntry {
|
|
||||||
binding: 1,
|
|
||||||
resource: storage_buffer.as_entire_binding(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[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<ColorStop>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Pipeline {
|
impl Pipeline {
|
||||||
/// Creates a new [GradientPipeline] using `gradient.wgsl` shader.
|
pub fn new(
|
||||||
pub(super) fn new(
|
|
||||||
device: &wgpu::Device,
|
device: &wgpu::Device,
|
||||||
format: wgpu::TextureFormat,
|
format: wgpu::TextureFormat,
|
||||||
antialiasing: Option<Antialiasing>,
|
antialiasing: Option<Antialiasing>,
|
||||||
|
|
@ -761,40 +608,15 @@ mod gradient {
|
||||||
let constants_layout = device.create_bind_group_layout(
|
let constants_layout = device.create_bind_group_layout(
|
||||||
&wgpu::BindGroupLayoutDescriptor {
|
&wgpu::BindGroupLayoutDescriptor {
|
||||||
label: Some(
|
label: Some(
|
||||||
"iced_wgpu::triangle::gradient bind group layout",
|
"iced_wgpu.triangle.gradient.bind_group_layout",
|
||||||
),
|
),
|
||||||
entries: &[
|
entries: &[triangle::Uniforms::entry()],
|
||||||
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,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let layout = device.create_pipeline_layout(
|
let layout = device.create_pipeline_layout(
|
||||||
&wgpu::PipelineLayoutDescriptor {
|
&wgpu::PipelineLayoutDescriptor {
|
||||||
label: Some(
|
label: Some("iced_wgpu.triangle.gradient.pipeline_layout"),
|
||||||
"iced_wgpu::triangle::gradient pipeline layout",
|
|
||||||
),
|
|
||||||
bind_group_layouts: &[&constants_layout],
|
bind_group_layouts: &[&constants_layout],
|
||||||
push_constant_ranges: &[],
|
push_constant_ranges: &[],
|
||||||
},
|
},
|
||||||
|
|
@ -802,48 +624,66 @@ mod gradient {
|
||||||
|
|
||||||
let shader =
|
let shader =
|
||||||
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
device.create_shader_module(wgpu::ShaderModuleDescriptor {
|
||||||
label: Some(
|
label: Some("iced_wgpu.triangle.gradient.shader"),
|
||||||
"iced_wgpu::triangle::gradient create shader module",
|
|
||||||
),
|
|
||||||
source: wgpu::ShaderSource::Wgsl(
|
source: wgpu::ShaderSource::Wgsl(
|
||||||
std::borrow::Cow::Borrowed(include_str!(
|
std::borrow::Cow::Borrowed(include_str!(
|
||||||
"shader/gradient.wgsl"
|
"shader/triangle.wgsl"
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
let pipeline =
|
let pipeline = device.create_render_pipeline(
|
||||||
device.create_render_pipeline(
|
&wgpu::RenderPipelineDescriptor {
|
||||||
&wgpu::RenderPipelineDescriptor {
|
label: Some("iced_wgpu.triangle.gradient.pipeline"),
|
||||||
label: Some("iced_wgpu::triangle::gradient pipeline"),
|
layout: Some(&layout),
|
||||||
layout: Some(&layout),
|
vertex: wgpu::VertexState {
|
||||||
vertex: wgpu::VertexState {
|
module: &shader,
|
||||||
module: &shader,
|
entry_point: "gradient_vs_main",
|
||||||
entry_point: "vs_main",
|
buffers: &[wgpu::VertexBufferLayout {
|
||||||
buffers: &[wgpu::VertexBufferLayout {
|
array_stride: std::mem::size_of::<
|
||||||
array_stride: std::mem::size_of::<
|
primitive::GradientVertex2D,
|
||||||
primitive::Vertex2D,
|
>()
|
||||||
>(
|
as u64,
|
||||||
)
|
step_mode: wgpu::VertexStepMode::Vertex,
|
||||||
as u64,
|
attributes: &wgpu::vertex_attr_array!(
|
||||||
step_mode: wgpu::VertexStepMode::Vertex,
|
// Position
|
||||||
attributes: &wgpu::vertex_attr_array!(
|
0 => Float32x2,
|
||||||
// Position
|
// Color 1
|
||||||
0 => Float32x2,
|
1 => Float32x4,
|
||||||
),
|
// Color 2
|
||||||
}],
|
2 => Float32x4,
|
||||||
},
|
// Color 3
|
||||||
fragment: Some(wgpu::FragmentState {
|
3 => Float32x4,
|
||||||
module: &shader,
|
// Color 4
|
||||||
entry_point: "fs_main",
|
4 => Float32x4,
|
||||||
targets: &[triangle::fragment_target(format)],
|
// Color 5
|
||||||
}),
|
5 => Float32x4,
|
||||||
primitive: triangle::primitive_state(),
|
// Color 6
|
||||||
depth_stencil: None,
|
6 => Float32x4,
|
||||||
multisample: triangle::multisample_state(antialiasing),
|
// Color 7
|
||||||
multiview: None,
|
7 => Float32x4,
|
||||||
|
// Color 8
|
||||||
|
8 => Float32x4,
|
||||||
|
// Offsets 1-4
|
||||||
|
9 => Float32x4,
|
||||||
|
// Offsets 5-8
|
||||||
|
10 => Float32x4,
|
||||||
|
// 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,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pipeline,
|
pipeline,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue