From 2adaf3f9db33c5150bb4dc2b84056810c4b0ae97 Mon Sep 17 00:00:00 2001 From: bungoboingo Date: Tue, 14 Mar 2023 20:39:39 -0700 Subject: [PATCH] Rebased PR to `advanced-text` --- core/Cargo.toml | 1 - core/src/angle.rs | 18 + core/src/gradient.rs | 58 +- core/src/gradient/linear.rs | 0 examples/modern_art/src/main.rs | 0 examples/tour/src/main.rs | 6 +- glow/src/program.rs | 0 glow/src/quad/compatibility.rs | 0 glow/src/quad/core.rs | 0 .../shader/quad/compatibility/gradient.frag | 158 ---- .../shader/quad/compatibility/gradient.vert | 72 -- glow/src/shader/quad/compatibility/solid.frag | 82 -- glow/src/shader/quad/compatibility/solid.vert | 45 -- glow/src/shader/quad/core/gradient.frag | 163 ---- glow/src/shader/quad/core/gradient.vert | 80 -- glow/src/shader/quad/core/solid.frag | 93 --- glow/src/shader/quad/core/solid.vert | 52 -- glow/src/shader/triangle/gradient.frag | 89 --- glow/src/shader/triangle/gradient.vert | 42 - glow/src/shader/triangle/solid.vert | 11 - glow/src/window/compositor.rs | 0 graphics/src/geometry.rs | 2 - graphics/src/geometry/fill.rs | 3 +- graphics/src/geometry/style.rs | 3 +- graphics/src/gradient.rs | 128 +++ graphics/src/lib.rs | 2 + graphics/src/primitive.rs | 25 +- graphics/src/triangle.rs | 1 - src/lib.rs | 4 +- style/src/button.rs | 2 +- style/src/theme.rs | 2 +- tiny_skia/src/backend.rs | 40 +- tiny_skia/src/geometry.rs | 2 +- wgpu/Cargo.toml | 4 - wgpu/src/buffer.rs | 281 +------ wgpu/src/buffer/dynamic.rs | 0 wgpu/src/buffer/static.rs | 107 --- wgpu/src/geometry.rs | 51 +- wgpu/src/image.rs | 46 +- wgpu/src/layer.rs | 46 +- wgpu/src/layer/mesh.rs | 10 +- wgpu/src/lib.rs | 2 - wgpu/src/quad.rs | 754 +++++++++--------- wgpu/src/triangle.rs | 310 ++++--- widget/src/canvas.rs | 1 + 45 files changed, 845 insertions(+), 1951 deletions(-) delete mode 100644 core/src/gradient/linear.rs delete mode 100644 examples/modern_art/src/main.rs delete mode 100644 glow/src/program.rs delete mode 100644 glow/src/quad/compatibility.rs delete mode 100644 glow/src/quad/core.rs delete mode 100644 glow/src/shader/quad/compatibility/gradient.frag delete mode 100644 glow/src/shader/quad/compatibility/gradient.vert delete mode 100644 glow/src/shader/quad/compatibility/solid.frag delete mode 100644 glow/src/shader/quad/compatibility/solid.vert delete mode 100644 glow/src/shader/quad/core/gradient.frag delete mode 100644 glow/src/shader/quad/core/gradient.vert delete mode 100644 glow/src/shader/quad/core/solid.frag delete mode 100644 glow/src/shader/quad/core/solid.vert delete mode 100644 glow/src/shader/triangle/gradient.frag delete mode 100644 glow/src/shader/triangle/gradient.vert delete mode 100644 glow/src/shader/triangle/solid.vert delete mode 100644 glow/src/window/compositor.rs create mode 100644 graphics/src/gradient.rs delete mode 100644 graphics/src/triangle.rs delete mode 100644 wgpu/src/buffer/dynamic.rs delete mode 100644 wgpu/src/buffer/static.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 45c450b3d2..af02132851 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -9,7 +9,6 @@ repository = "https://github.com/iced-rs/iced" [dependencies] bitflags = "1.2" -thiserror = "1" twox-hash = { version = "1.5", default-features = false } [dependencies.palette] diff --git a/core/src/angle.rs b/core/src/angle.rs index 04ca50ffde..75a57c7662 100644 --- a/core/src/angle.rs +++ b/core/src/angle.rs @@ -1,3 +1,4 @@ +use crate::{Point, Rectangle, Vector}; use std::f32::consts::PI; #[derive(Debug, Copy, Clone, PartialEq)] @@ -13,3 +14,20 @@ impl From for Radians { Radians(degrees.0 * PI / 180.0) } } + +impl Radians { + /// Calculates the line in which the [`Angle`] intercepts the `bounds`. + pub fn to_distance(&self, bounds: &Rectangle) -> (Point, Point) { + let v1 = Vector::new(f32::cos(self.0), f32::sin(self.0)); + + let distance_to_rect = f32::min( + f32::abs((bounds.y - bounds.center().y) / v1.y), + f32::abs(((bounds.x + bounds.width) - bounds.center().x) / v1.x), + ); + + let start = bounds.center() + v1 * distance_to_rect; + let end = bounds.center() - v1 * distance_to_rect; + + (start, end) + } +} diff --git a/core/src/gradient.rs b/core/src/gradient.rs index 843da108fd..7e90c2d83f 100644 --- a/core/src/gradient.rs +++ b/core/src/gradient.rs @@ -1,7 +1,7 @@ //! For creating a Gradient. pub use linear::Linear; -use crate::{Color, Radians, Rectangle, Vector}; +use crate::{Color, Radians}; #[derive(Debug, Clone, Copy, PartialEq)] /// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), @@ -23,7 +23,7 @@ impl Gradient { } /// Adjust the opacity of the gradient by a multiplier applied to each color stop. - pub fn transparentize(mut self, alpha_multiplier: f32) -> Self { + pub fn mul_alpha(mut self, alpha_multiplier: f32) -> Self { match &mut self { Gradient::Linear(linear) => { for stop in &mut linear.color_stops { @@ -34,60 +34,6 @@ impl Gradient { self } - - /// Packs the [`Gradient`] into a buffer for use in shader code. - pub fn pack(&self, bounds: Rectangle) -> [f32; 44] { - match self { - Gradient::Linear(linear) => { - let mut pack: [f32; 44] = [0.0; 44]; - let mut offsets: [f32; 8] = [2.0; 8]; - - for (index, stop) in - linear.color_stops.iter().enumerate().take(8) - { - let [r, g, b, a] = stop.color.into_linear(); - - pack[(index * 4)] = r; - pack[(index * 4) + 1] = g; - pack[(index * 4) + 2] = b; - pack[(index * 4) + 3] = a; - - offsets[index] = stop.offset; - } - - pack[32] = offsets[0]; - pack[33] = offsets[1]; - pack[34] = offsets[2]; - pack[35] = offsets[3]; - pack[36] = offsets[4]; - pack[37] = offsets[5]; - pack[38] = offsets[6]; - pack[39] = offsets[7]; - - let v1 = Vector::new( - f32::cos(linear.angle.0), - f32::sin(linear.angle.0), - ); - - let distance_to_rect = f32::min( - f32::abs((bounds.y - bounds.center().y) / v1.y), - f32::abs( - ((bounds.x + bounds.width) - bounds.center().x) / v1.x, - ), - ); - - let start = bounds.center() + v1 * distance_to_rect; - let end = bounds.center() - v1 * distance_to_rect; - - pack[40] = start.x; - pack[41] = start.y; - pack[42] = end.x; - pack[43] = end.y; - - pack - } - } - } } #[derive(Debug, Default, Clone, Copy, PartialEq)] diff --git a/core/src/gradient/linear.rs b/core/src/gradient/linear.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/modern_art/src/main.rs b/examples/modern_art/src/main.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/examples/tour/src/main.rs b/examples/tour/src/main.rs index 3b86ec4135..13a84ec6f6 100644 --- a/examples/tour/src/main.rs +++ b/examples/tour/src/main.rs @@ -4,8 +4,10 @@ use iced::widget::{ scrollable, slider, text, text_input, toggler, vertical_space, }; use iced::widget::{Button, Column, Container, Slider}; -use iced::{alignment, widget, Degrees, Gradient, Radians, Theme}; -use iced::{Color, Element, Length, Renderer, Sandbox, Settings}; +use iced::{ + alignment, widget, Color, Degrees, Element, Gradient, Length, Radians, + Renderer, Sandbox, Settings, Theme, +}; pub fn main() -> iced::Result { env_logger::init(); diff --git a/glow/src/program.rs b/glow/src/program.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/glow/src/quad/compatibility.rs b/glow/src/quad/compatibility.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/glow/src/quad/core.rs b/glow/src/quad/core.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/glow/src/shader/quad/compatibility/gradient.frag b/glow/src/shader/quad/compatibility/gradient.frag deleted file mode 100644 index 97c40d0942..0000000000 --- a/glow/src/shader/quad/compatibility/gradient.frag +++ /dev/null @@ -1,158 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -float random(vec2 coords) { - return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float noise_granularity = 0.3/255.0; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color += mix(-noise_granularity, noise_granularity, random(raw_position)); -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -varying vec4 v_colors[8]; -varying vec4 v_offsets[2]; -varying vec4 v_direction; -varying vec4 v_position_and_size; -varying vec4 v_border_color; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - vec2 v_position = v_position_and_size.xy; - vec2 v_size = v_position_and_size.zw; - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_size * 0.5).xy - ); - - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_size - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - vec4 mixed_color = mix( - gradient(v_direction, fragCoord, v_offsets, v_colors), - v_border_color, - border_mix - ); - - float d = fDistance( - fragCoord, - v_position, - v_size, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/compatibility/gradient.vert b/glow/src/shader/quad/compatibility/gradient.vert deleted file mode 100644 index 49696be2a7..0000000000 --- a/glow/src/shader/quad/compatibility/gradient.vert +++ /dev/null @@ -1,72 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -//gradient -attribute vec4 i_colors_1; -attribute vec4 i_colors_2; -attribute vec4 i_colors_3; -attribute vec4 i_colors_4; -attribute vec4 i_colors_5; -attribute vec4 i_colors_6; -attribute vec4 i_colors_7; -attribute vec4 i_colors_8; -attribute vec4 i_offsets_1; -attribute vec4 i_offsets_2; -attribute vec4 i_direction; -//quad properties -attribute vec4 i_position_and_size; -attribute vec4 i_border_color; -attribute vec4 i_border_radius; -attribute float i_border_width; -attribute vec2 i_quad_position; - -varying vec4 v_colors[8]; -varying vec4 v_offsets[2]; -varying vec4 v_direction; -varying vec4 v_position_and_size; -varying vec4 v_border_color; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 i_position = i_position_and_size.xy; - vec2 i_size = i_position_and_size.zw; - - vec2 p_position = i_position * u_scale; - vec2 p_size = i_size * u_scale; - - vec4 i_border_radius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_size.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - // array initializers are not supported in GLSL 1.0 (ES 2.0) - v_colors[0] = i_colors_1; - v_colors[1] = i_colors_2; - v_colors[2] = i_colors_3; - v_colors[3] = i_colors_4; - v_colors[4] = i_colors_5; - v_colors[5] = i_colors_6; - v_colors[6] = i_colors_7; - v_colors[7] = i_colors_8; - - v_offsets[0] = i_offsets_1; - v_offsets[1] = i_offsets_2; - - v_direction = i_direction * u_scale; - v_position_and_size = vec4(p_position, p_size); - v_border_color = i_border_color; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(i_quad_position, 0.0, 1.0); -} diff --git a/glow/src/shader/quad/compatibility/solid.frag b/glow/src/shader/quad/compatibility/solid.frag deleted file mode 100644 index 42078b3513..0000000000 --- a/glow/src/shader/quad/compatibility/solid.frag +++ /dev/null @@ -1,82 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -varying vec4 v_color; -varying vec4 v_border_color; -varying vec2 v_position; -varying vec2 v_size; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_size * 0.5).xy - ); - - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_size - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - vec4 mixed_color = mix(v_color, v_border_color, border_mix); - - float d = fDistance( - fragCoord, - v_position, - v_size, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/compatibility/solid.vert b/glow/src/shader/quad/compatibility/solid.vert deleted file mode 100644 index bbac512eb6..0000000000 --- a/glow/src/shader/quad/compatibility/solid.vert +++ /dev/null @@ -1,45 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -attribute vec4 i_color; -attribute vec2 i_position; -attribute vec2 i_size; -attribute vec4 i_border_color; -attribute vec4 i_border_radius; -attribute float i_border_width; -attribute vec2 i_quad_position; - -varying vec4 v_color; -varying vec2 v_position; -varying vec2 v_size; -varying vec4 v_border_color; -varying vec4 v_border_radius; -varying float v_border_width; - -void main() { - vec2 p_position = i_position * u_scale; - vec2 p_size = i_size * u_scale; - - vec4 i_border_radius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_size.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - v_color = i_color; - v_border_color = i_border_color; - v_position = p_position; - v_size = p_size; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(i_quad_position, 0.0, 1.0); -} diff --git a/glow/src/shader/quad/core/gradient.frag b/glow/src/shader/quad/core/gradient.frag deleted file mode 100644 index 984c399869..0000000000 --- a/glow/src/shader/quad/core/gradient.frag +++ /dev/null @@ -1,163 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -#ifdef HIGHER_THAN_300 -out vec4 fragColor; -#define gl_FragColor fragColor -#endif - -float random(vec2 coords) { - return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float noise_granularity = 0.3/255.0; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color += mix(-noise_granularity, noise_granularity, random(raw_position)); -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -in vec4 v_colors[8]; -in vec4 v_offsets[2]; -in vec4 v_direction; -in vec4 v_position_and_size; -in vec4 v_border_color; -in vec4 v_border_radius; -in float v_border_width; - -void main() { - vec2 v_position = v_position_and_size.xy; - vec2 v_size = v_position_and_size.zw; - - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - vec4 mixed_color = gradient(v_direction, fragCoord, v_offsets, v_colors); - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_size * 0.5).xy - ); - - if (v_border_width > 0.0) { - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_size - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - mixed_color = mix(mixed_color, v_border_color, border_mix); - } - - float d = fDistance( - fragCoord, - v_position, - v_size, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/core/gradient.vert b/glow/src/shader/quad/core/gradient.vert deleted file mode 100644 index c003889370..0000000000 --- a/glow/src/shader/quad/core/gradient.vert +++ /dev/null @@ -1,80 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -in vec4 i_colors_1; -in vec4 i_colors_2; -in vec4 i_colors_3; -in vec4 i_colors_4; -in vec4 i_colors_5; -in vec4 i_colors_6; -in vec4 i_colors_7; -in vec4 i_colors_8; -in vec4 i_offsets_1; -in vec4 i_offsets_2; -in vec4 i_direction; -in vec4 i_position_and_size; -in vec4 i_border_color; -in vec4 i_border_radius; -in float i_border_width; - -out vec4 v_colors[8]; -out vec4 v_offsets[2]; -out vec4 v_direction; -out vec4 v_position_and_size; -out vec4 v_border_color; -out vec4 v_border_radius; -out float v_border_width; - -vec2 positions[4] = vec2[]( - vec2(0.0, 0.0), - vec2(0.0, 1.0), - vec2(1.0, 0.0), - vec2(1.0, 1.0) -); - -void main() { - vec2 i_position = i_position_and_size.xy; - vec2 i_size = i_position_and_size.zw; - - vec2 q_position = positions[gl_VertexID]; - vec2 p_position = i_position * u_scale; - vec2 p_size = i_size * u_scale; - - vec4 i_border_radius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_size.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_size.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - v_colors = vec4[]( - i_colors_1, - i_colors_2, - i_colors_3, - i_colors_4, - i_colors_5, - i_colors_6, - i_colors_7, - i_colors_8 - ); - - v_offsets = vec4[]( - i_offsets_1, - i_offsets_2 - ); - - v_direction = i_direction * u_scale; - v_position_and_size = vec4(p_position, p_size); - v_border_color = i_border_color; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(q_position, 0.0, 1.0); -} diff --git a/glow/src/shader/quad/core/solid.frag b/glow/src/shader/quad/core/solid.frag deleted file mode 100644 index ffb3b08ae6..0000000000 --- a/glow/src/shader/quad/core/solid.frag +++ /dev/null @@ -1,93 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -#ifdef HIGHER_THAN_300 -out vec4 fragColor; -#define gl_FragColor fragColor -#endif - -float selectBorderRadius(vec4 radi, vec2 position, vec2 center) -{ - float rx = radi.x; - float ry = radi.y; - rx = position.x > center.x ? radi.y : radi.x; - ry = position.x > center.x ? radi.z : radi.w; - rx = position.y > center.y ? ry : rx; - return rx; -} - -float fDistance(vec2 frag_coord, vec2 position, vec2 size, float radius) -{ - // TODO: Try SDF approach: https://www.shadertoy.com/view/wd3XRN - vec2 inner_size = size - vec2(radius, radius) * 2.0; - vec2 top_left = position + vec2(radius, radius); - vec2 bottom_right = top_left + inner_size; - - vec2 top_left_distance = top_left - frag_coord; - vec2 bottom_right_distance = frag_coord - bottom_right; - - vec2 distance = vec2( - max(max(top_left_distance.x, bottom_right_distance.x), 0.0), - max(max(top_left_distance.y, bottom_right_distance.y), 0.0) - ); - - return sqrt(distance.x * distance.x + distance.y * distance.y); -} - -uniform float u_screen_height; - -in vec4 v_color; -in vec4 v_border_color; -in vec2 v_position; -in vec2 v_scale; -in vec4 v_border_radius; -in float v_border_width; - -void main() { - vec4 mixed_color; - - vec2 fragCoord = vec2(gl_FragCoord.x, u_screen_height - gl_FragCoord.y); - - float border_radius = selectBorderRadius( - v_border_radius, - fragCoord, - (v_position + v_scale * 0.5).xy - ); - - if (v_border_width > 0.0) { - float internal_border = max(border_radius - v_border_width, 0.0); - - float internal_distance = fDistance( - fragCoord, - v_position + vec2(v_border_width), - v_scale - vec2(v_border_width * 2.0), - internal_border - ); - - float border_mix = smoothstep( - max(internal_border - 0.5, 0.0), - internal_border + 0.5, - internal_distance - ); - - mixed_color = mix(v_color, v_border_color, border_mix); - } else { - mixed_color = v_color; - } - - float d = fDistance( - fragCoord, - v_position, - v_scale, - border_radius - ); - - float radius_alpha = 1.0 - smoothstep(max(border_radius - 0.5, 0.0), border_radius + 0.5, d); - - gl_FragColor = vec4(mixed_color.xyz, mixed_color.w * radius_alpha); -} diff --git a/glow/src/shader/quad/core/solid.vert b/glow/src/shader/quad/core/solid.vert deleted file mode 100644 index 2e5f1dd207..0000000000 --- a/glow/src/shader/quad/core/solid.vert +++ /dev/null @@ -1,52 +0,0 @@ -uniform mat4 u_transform; -uniform float u_scale; - -in vec4 i_color; -in vec2 i_position; -in vec2 i_size; -in vec4 i_border_color; -in vec4 i_border_radius; -in float i_border_width; - -out vec4 v_color; -out vec4 v_border_color; -out vec2 v_position; -out vec2 v_scale; -out vec4 v_border_radius; -out float v_border_width; - -vec2 positions[4] = vec2[]( - vec2(0.0, 0.0), - vec2(0.0, 1.0), - vec2(1.0, 0.0), - vec2(1.0, 1.0) -); - -void main() { - vec2 q_position = positions[gl_VertexID]; - vec2 p_position = i_position * u_scale; - vec2 p_scale = i_size * u_scale; - - vec4 i_BorderRadius = vec4( - min(i_border_radius.x, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.y, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.z, min(i_size.x, i_size.y) / 2.0), - min(i_border_radius.w, min(i_size.x, i_size.y) / 2.0) - ); - - mat4 i_transform = mat4( - vec4(p_scale.x + 1.0, 0.0, 0.0, 0.0), - vec4(0.0, p_scale.y + 1.0, 0.0, 0.0), - vec4(0.0, 0.0, 1.0, 0.0), - vec4(p_position - vec2(0.5, 0.5), 0.0, 1.0) - ); - - v_color = i_color; - v_border_color = i_border_color; - v_position = p_position; - v_scale = p_scale; - v_border_radius = i_border_radius * u_scale; - v_border_width = i_border_width * u_scale; - - gl_Position = u_transform * i_transform * vec4(q_position, 0.0, 1.0); -} diff --git a/glow/src/shader/triangle/gradient.frag b/glow/src/shader/triangle/gradient.frag deleted file mode 100644 index 435af3d0ee..0000000000 --- a/glow/src/shader/triangle/gradient.frag +++ /dev/null @@ -1,89 +0,0 @@ -#ifdef GL_ES -#ifdef GL_FRAGMENT_PRECISION_HIGH -precision highp float; -#else -precision mediump float; -#endif -#endif - -#ifdef HIGHER_THAN_300 -out vec4 fragColor; -#define gl_FragColor fragColor -#endif - -float random(vec2 coords) { - return fract(sin(dot(coords.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -vec4 gradient( - vec4 direction, - vec2 raw_position, - vec4 offsets[2], - vec4 colors[8] -) { - vec2 start = direction.xy; - vec2 end = direction.zw; - - vec2 v1 = vec2(end - start); - vec2 v2 = vec2(raw_position - start); - vec2 unit = normalize(v1); - float coord_offset = dot(unit, v2) / length(v1); - - //if a gradient has a start/end stop that is identical, the mesh will have a transparent fill - vec4 color; - - float noise_granularity = 0.3/255.0; - - float offsets_arr[8]; - offsets_arr[0] = offsets[0].x; - offsets_arr[1] = offsets[0].y; - offsets_arr[2] = offsets[0].z; - offsets_arr[3] = offsets[0].w; - offsets_arr[4] = offsets[1].x; - offsets_arr[5] = offsets[1].y; - offsets_arr[6] = offsets[1].z; - offsets_arr[7] = offsets[1].w; - - int last_index = 7; - for (int i = 0; i <= 7; i++) { - if (offsets_arr[i] > 1.0) { - last_index = i - 1; - break; - } - } - - for (int i = 0; i < last_index; i++) { - float curr_offset = offsets_arr[i]; - float next_offset = offsets_arr[i+1]; - - if (coord_offset <= offsets_arr[0]) { - //current coordinate is before the first defined offset, set it to the start color - color = colors[0]; - } - - if (curr_offset <= coord_offset && coord_offset <= next_offset) { - //current fragment is between the current offset & the next one, interpolate colors - color = mix(colors[i], colors[i+1], smoothstep( - curr_offset, - next_offset, - coord_offset - )); - } - - if (coord_offset >= offsets_arr[last_index]) { - //current coordinate is after the last defined offset, set it to the last color - color = colors[last_index]; - } - } - - return color += mix(-noise_granularity, noise_granularity, random(raw_position)); -} - -in vec2 v_raw_position; -in vec4 v_colors[8]; -in vec4 v_offsets[2]; -in vec4 v_direction; - -void main() { - gl_FragColor = gradient(v_direction, v_raw_position, v_offsets, v_colors); -} diff --git a/glow/src/shader/triangle/gradient.vert b/glow/src/shader/triangle/gradient.vert deleted file mode 100644 index ab8c59f330..0000000000 --- a/glow/src/shader/triangle/gradient.vert +++ /dev/null @@ -1,42 +0,0 @@ -uniform mat4 u_transform; - -in vec2 i_position; -in vec4 i_colors_1; -in vec4 i_colors_2; -in vec4 i_colors_3; -in vec4 i_colors_4; -in vec4 i_colors_5; -in vec4 i_colors_6; -in vec4 i_colors_7; -in vec4 i_colors_8; -in vec4 i_offsets_1; -in vec4 i_offsets_2; -in vec4 i_direction; - -out vec2 v_raw_position; -out vec4 v_colors[8]; -out vec4 v_offsets[2]; -out vec4 v_direction; - -void main() { - gl_Position = u_transform * vec4(i_position, 0.0, 1.0); - - v_raw_position = i_position; - - v_colors[0] = i_colors_1; - - // array initializers are not supported in GLSL 1.0 (ES 2.0) - v_colors[0] = i_colors_1; - v_colors[1] = i_colors_2; - v_colors[2] = i_colors_3; - v_colors[3] = i_colors_4; - v_colors[4] = i_colors_5; - v_colors[5] = i_colors_6; - v_colors[6] = i_colors_7; - v_colors[7] = i_colors_8; - - v_offsets[0] = i_offsets_1; - v_offsets[1] = i_offsets_2; - - v_direction = i_direction; -} diff --git a/glow/src/shader/triangle/solid.vert b/glow/src/shader/triangle/solid.vert deleted file mode 100644 index d0e6d780cb..0000000000 --- a/glow/src/shader/triangle/solid.vert +++ /dev/null @@ -1,11 +0,0 @@ -uniform mat4 u_transform; - -in vec2 i_position; -in vec4 i_color; - -out vec4 v_color; - -void main() { - gl_Position = u_transform * vec4(i_position, 0.0, 1.0); - v_color = i_color; -} diff --git a/glow/src/window/compositor.rs b/glow/src/window/compositor.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/graphics/src/geometry.rs b/graphics/src/geometry.rs index 8db1594a72..afb4755bfa 100644 --- a/graphics/src/geometry.rs +++ b/graphics/src/geometry.rs @@ -16,8 +16,6 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke}; pub use style::Style; pub use text::Text; -pub use iced_core::gradient::{self, Gradient}; - use crate::Primitive; #[derive(Debug, Clone)] diff --git a/graphics/src/geometry/fill.rs b/graphics/src/geometry/fill.rs index 2e8c1669be..025350cde1 100644 --- a/graphics/src/geometry/fill.rs +++ b/graphics/src/geometry/fill.rs @@ -1,5 +1,6 @@ //! Fill [crate::widget::canvas::Geometry] with a certain style. -use iced_core::{Color, Gradient}; +use crate::core::Color; +use crate::Gradient; pub use crate::geometry::Style; diff --git a/graphics/src/geometry/style.rs b/graphics/src/geometry/style.rs index be9ee376de..866bf13276 100644 --- a/graphics/src/geometry/style.rs +++ b/graphics/src/geometry/style.rs @@ -1,4 +1,5 @@ -use iced_core::{Color, Gradient}; +use crate::core::Color; +use crate::Gradient; /// The coloring style of some drawing. #[derive(Debug, Clone, PartialEq)] diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs new file mode 100644 index 0000000000..adfe414f2d --- /dev/null +++ b/graphics/src/gradient.rs @@ -0,0 +1,128 @@ +//! For creating a Gradient that can be used as a [`Fill`] for a mesh. +use crate::core::Point; +pub use linear::Linear; + +#[derive(Debug, Clone, PartialEq)] +/// A fill which transitions colors progressively along a direction, either linearly, radially (TBD), +/// or conically (TBD). +/// +/// For a gradient which can be used as a fill for a background of a widget, see [`core::Gradient`]. +pub enum Gradient { + /// A linear gradient interpolates colors along a direction from its `start` to its `end` + /// point. + Linear(Linear), +} + +impl Gradient { + /// Creates a new linear [`linear::Builder`]. + /// + /// The `start` and `end` [`Point`]s define the absolute position of the [`Gradient`]. + pub fn linear(start: Point, end: Point) -> linear::Builder { + linear::Builder::new(start, end) + } +} + +pub mod linear { + //! Linear gradient builder & definition. + use crate::Gradient; + use iced_core::gradient::linear::BuilderError; + use iced_core::gradient::ColorStop; + use iced_core::{Color, Point}; + + /// A linear gradient that can be used in the style of [`Fill`] or [`Stroke`]. + /// + /// [`Fill`]: crate::widget::canvas::Fill + /// [`Stroke`]: crate::widget::canvas::Stroke + #[derive(Debug, Clone, Copy, PartialEq)] + pub struct Linear { + /// The absolute starting position of the gradient. + pub start: Point, + + /// The absolute ending position of the gradient. + pub end: Point, + + /// [`ColorStop`]s along the linear gradient path. + pub color_stops: [ColorStop; 8], + } + + /// A [`Linear`] builder. + #[derive(Debug)] + pub struct Builder { + start: Point, + end: Point, + stops: [ColorStop; 8], + error: Option, + } + + impl Builder { + /// Creates a new [`Builder`]. + pub fn new(start: Point, end: Point) -> Self { + Self { + start, + end, + stops: std::array::from_fn(|_| ColorStop { + offset: 2.0, //default offset = invalid + color: Default::default(), + }), + error: None, + } + } + + /// Adds a new [`ColorStop`], defined by an offset and a color, to the gradient. + /// + /// `offset` must be between `0.0` and `1.0` or the gradient cannot be built. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stop(mut self, offset: f32, color: Color) -> Self { + if offset.is_finite() && (0.0..=1.0).contains(&offset) { + match self.stops.binary_search_by(|stop| { + stop.offset.partial_cmp(&offset).unwrap() + }) { + Ok(_) => { + self.error = Some(BuilderError::DuplicateOffset(offset)) + } + Err(index) => { + if index < 8 { + self.stops[index] = ColorStop { offset, color }; + } + } + } + } else { + self.error = Some(BuilderError::InvalidOffset(offset)) + }; + + self + } + + /// Adds multiple [`ColorStop`]s to the gradient. + /// + /// Any stop added after the 8th will be silently ignored. + pub fn add_stops( + mut self, + stops: impl IntoIterator, + ) -> Self { + for stop in stops.into_iter() { + self = self.add_stop(stop.offset, stop.color) + } + + self + } + + /// Builds the linear [`Gradient`] of this [`Builder`]. + /// + /// Returns `BuilderError` if gradient in invalid. + pub fn build(self) -> Result { + if self.stops.is_empty() { + Err(BuilderError::MissingColorStop) + } else if let Some(error) = self.error { + Err(error) + } else { + Ok(Gradient::Linear(Linear { + start: self.start, + end: self.end, + color_stops: self.stops, + })) + } + } + } +} diff --git a/graphics/src/lib.rs b/graphics/src/lib.rs index 0c50db52d5..a7d1541a19 100644 --- a/graphics/src/lib.rs +++ b/graphics/src/lib.rs @@ -23,6 +23,7 @@ #![cfg_attr(docsrs, feature(doc_cfg))] mod antialiasing; mod error; +mod gradient; mod transformation; mod viewport; @@ -41,6 +42,7 @@ pub use antialiasing::Antialiasing; pub use backend::Backend; pub use compositor::Compositor; pub use error::Error; +pub use gradient::Gradient; pub use primitive::Primitive; pub use renderer::Renderer; pub use transformation::Transformation; diff --git a/graphics/src/primitive.rs b/graphics/src/primitive.rs index 50f38d6b70..7050e5fc4c 100644 --- a/graphics/src/primitive.rs +++ b/graphics/src/primitive.rs @@ -1,7 +1,7 @@ use iced_core::alignment; use iced_core::image; use iced_core::svg; -use iced_core::{Background, Color, Font, Gradient, Rectangle, Size, Vector}; +use iced_core::{Background, Color, Font, Rectangle, Size, Vector}; use bytemuck::{Pod, Zeroable}; use std::sync::Arc; @@ -158,25 +158,34 @@ pub struct Mesh2D { pub indices: Vec, } -/// A two-dimensional vertex. +/// A two-dimensional vertex with a color. #[derive(Copy, Clone, Debug, Zeroable, Pod)] #[repr(C)] -pub struct Vertex2D { +pub struct ColoredVertex2D { /// The vertex position in 2D space. pub position: [f32; 2], + + /// The color of the vertex in __linear__ RGBA. + pub color: [f32; 4], } -/// A two-dimensional vertex with a color. -#[derive(Copy, Clone, Debug, Zeroable, Pod)] +/// A vertex which contains 2D position & packed gradient data. +#[derive(Copy, Clone, Debug)] #[repr(C)] -pub struct ColoredVertex2D { +pub struct GradientVertex2D { /// The vertex position in 2D space. pub position: [f32; 2], - /// The color of the vertex in __linear__ RGBA. - pub color: [f32; 4], + /// The packed vertex data of the gradient. + pub gradient: [f32; 44], } +#[allow(unsafe_code)] +unsafe impl Zeroable for GradientVertex2D {} + +#[allow(unsafe_code)] +unsafe impl Pod for GradientVertex2D {} + impl From<()> for Primitive { fn from(_: ()) -> Self { Self::Group { primitives: vec![] } diff --git a/graphics/src/triangle.rs b/graphics/src/triangle.rs deleted file mode 100644 index 09b6176753..0000000000 --- a/graphics/src/triangle.rs +++ /dev/null @@ -1 +0,0 @@ -//! Draw geometry using meshes of triangles. diff --git a/src/lib.rs b/src/lib.rs index c59d505804..e441e27b48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -189,8 +189,8 @@ pub use style::theme; pub use crate::core::alignment; pub use crate::core::event; pub use crate::core::{ - color, Alignment, Background, Color, ContentFit, Length, Padding, Point, - Rectangle, Size, Vector, + color, Alignment, Background, Color, ContentFit, Degrees, Gradient, Length, + Padding, Point, Radians, Rectangle, Size, Vector, }; pub use crate::runtime::Command; diff --git a/style/src/button.rs b/style/src/button.rs index 31570af052..32ec28b711 100644 --- a/style/src/button.rs +++ b/style/src/button.rs @@ -69,7 +69,7 @@ pub trait StyleSheet { ..color }), Background::Gradient(gradient) => { - Background::Gradient(gradient.transparentize(0.5)) + Background::Gradient(gradient.mul_alpha(0.5)) } }), text_color: Color { diff --git a/style/src/theme.rs b/style/src/theme.rs index 6993e415f1..dd7ca45a12 100644 --- a/style/src/theme.rs +++ b/style/src/theme.rs @@ -218,7 +218,7 @@ impl button::StyleSheet for Theme { ..color }), Background::Gradient(gradient) => { - Background::Gradient(gradient.transparentize(0.5)) + Background::Gradient(gradient.mul_alpha(0.5)) } }), text_color: Color { diff --git a/tiny_skia/src/backend.rs b/tiny_skia/src/backend.rs index 271d026f15..35427879d8 100644 --- a/tiny_skia/src/backend.rs +++ b/tiny_skia/src/backend.rs @@ -1,5 +1,5 @@ -use crate::core::alignment; use crate::core::text; +use crate::core::{alignment, Gradient}; use crate::core::{Background, Color, Font, Point, Rectangle, Size, Vector}; use crate::graphics::backend; use crate::graphics::{Primitive, Viewport}; @@ -128,6 +128,9 @@ impl Backend { *color, )) } + Background::Gradient(gradient) => { + into_gradient(*gradient, *bounds) + } }, anti_alias: true, ..tiny_skia::Paint::default() @@ -318,6 +321,41 @@ fn into_color(color: Color) -> tiny_skia::Color { .expect("Convert color from iced to tiny_skia") } +fn into_gradient<'a>( + gradient: Gradient, + bounds: Rectangle, +) -> tiny_skia::Shader<'a> { + let Gradient::Linear(linear) = gradient; + let (start, end) = linear.angle.to_distance(&bounds); + + tiny_skia::LinearGradient::new( + tiny_skia::Point { + x: start.x, + y: start.y, + }, + tiny_skia::Point { x: end.x, y: end.y }, + linear + .color_stops + .into_iter() + .map(|stop| { + tiny_skia::GradientStop::new( + stop.offset, + tiny_skia::Color::from_rgba( + stop.color.b, + stop.color.g, + stop.color.r, + stop.color.a, + ) + .expect("Create color"), + ) + }) + .collect(), + tiny_skia::SpreadMode::Pad, + tiny_skia::Transform::identity(), + ) + .expect("Create linear gradient") +} + fn rounded_rectangle( bounds: Rectangle, border_radius: [f32; 4], diff --git a/tiny_skia/src/geometry.rs b/tiny_skia/src/geometry.rs index c66621dd8c..951c48a41b 100644 --- a/tiny_skia/src/geometry.rs +++ b/tiny_skia/src/geometry.rs @@ -1,8 +1,8 @@ -use crate::core::Gradient; use crate::core::{Point, Rectangle, Size, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::stroke::{self, Stroke}; use crate::graphics::geometry::{Path, Style, Text}; +use crate::graphics::Gradient; use crate::graphics::Primitive; pub struct Frame { diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 3aef4ff4de..f5e86dbb07 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -46,10 +46,6 @@ version = "0.2" git = "https://github.com/hecrj/glyphon.git" rev = "47050174841a4f58fc8d85c943a2117f72f19e8e" -[dependencies.encase] -version = "0.3.0" -features = ["glam"] - [dependencies.glam] version = "0.21.3" diff --git a/wgpu/src/buffer.rs b/wgpu/src/buffer.rs index ecfca1025b..9412218729 100644 --- a/wgpu/src/buffer.rs +++ b/wgpu/src/buffer.rs @@ -6,7 +6,8 @@ pub struct Buffer { label: &'static str, size: u64, usage: wgpu::BufferUsages, - raw: wgpu::Buffer, + pub(crate) raw: wgpu::Buffer, + offsets: Vec, type_: PhantomData, } @@ -31,6 +32,7 @@ impl Buffer { size, usage, raw, + offsets: Vec::new(), type_: PhantomData, } } @@ -39,6 +41,8 @@ impl Buffer { let new_size = (std::mem::size_of::() * new_count) as u64; if self.size < new_size { + self.offsets.clear(); + self.raw = device.create_buffer(&wgpu::BufferDescriptor { label: Some(self.label), size: new_size, @@ -54,17 +58,19 @@ impl Buffer { } } + /// Returns the size of the written bytes. pub fn write( - &self, + &mut self, queue: &wgpu::Queue, - offset_count: usize, + offset: usize, contents: &[T], - ) { - queue.write_buffer( - &self.raw, - (std::mem::size_of::() * offset_count) as u64, - bytemuck::cast_slice(contents), - ); + ) -> usize { + let bytes: &[u8] = bytemuck::cast_slice(contents); + queue.write_buffer(&self.raw, offset as u64, bytes); + + self.offsets.push(offset as u64); + + bytes.len() } pub fn slice( @@ -73,263 +79,28 @@ impl Buffer { ) -> wgpu::BufferSlice<'_> { self.raw.slice(bounds) } -} - -fn next_copy_size(amount: usize) -> u64 { - let align_mask = wgpu::COPY_BUFFER_ALIGNMENT - 1; - - (((std::mem::size_of::() * amount).next_power_of_two() as u64 - + align_mask) - & !align_mask) - .max(wgpu::COPY_BUFFER_ALIGNMENT) -} - -//TODO(shan) -/// A generic buffer struct useful for items which have no alignment requirements -/// (e.g. Vertex, Index buffers) & no dynamic offsets. -#[derive(Debug)] -pub struct Static { - //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer - offsets: Vec, - label: &'static str, - usages: wgpu::BufferUsages, - gpu: wgpu::Buffer, - size: wgpu::BufferAddress, - _data: PhantomData, -} - -impl Static { - /// Initialize a new static buffer. - pub fn new( - device: &wgpu::Device, - label: &'static str, - usages: wgpu::BufferUsages, - count: usize, - ) -> Self { - let size = (mem::size_of::() * count) as u64; - - Self { - offsets: Vec::new(), - label, - usages, - gpu: Self::gpu_buffer(device, label, size, usages), - size, - _data: PhantomData, - } - } - - fn gpu_buffer( - device: &wgpu::Device, - label: &'static str, - size: wgpu::BufferAddress, - usage: wgpu::BufferUsages, - ) -> wgpu::Buffer { - device.create_buffer(&wgpu::BufferDescriptor { - label: Some(label), - size, - usage, - mapped_at_creation: false, - }) - } - - /// Returns whether or not the buffer needs to be recreated. - pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool { - let size = (mem::size_of::() * new_count) as u64; - - if self.size < size { - self.offsets.clear(); - self.size = size; - self.gpu = Self::gpu_buffer(device, self.label, size, self.usages); - true - } else { - false - } - } - - /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. - /// - /// Returns the size of the written bytes. - pub fn write( - &mut self, - device: &wgpu::Device, - staging_belt: &mut wgpu::util::StagingBelt, - encoder: &mut wgpu::CommandEncoder, - offset: u64, - content: &[T], - ) -> u64 { - let bytes = bytemuck::cast_slice(content); - let bytes_size = bytes.len() as u64; - - if let Some(buffer_size) = wgpu::BufferSize::new(bytes_size) { - let mut buffer = staging_belt.write_buffer( - encoder, - &self.gpu, - offset, - buffer_size, - device, - ); - - buffer.copy_from_slice(bytes); - - self.offsets.push(offset); - } - - bytes_size - } - - fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { - self.offsets - .get(index) - .expect("Offset at index does not exist.") - } /// Returns the slice calculated from the offset stored at the given index. - /// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index - /// 1 that we stored earlier when writing. pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { - self.gpu.slice(self.offset_at(index)..) + self.raw.slice(self.offset_at(index)..) } - /// Clears any temporary data from the buffer. + /// Clears any temporary data (i.e. offsets) from the buffer. pub fn clear(&mut self) { self.offsets.clear() } - /// Returns a reference to the GPU buffer. - pub fn raw(&self) -> &wgpu::Buffer { - &self.gpu - } -} - -/// A dynamic uniform buffer is any type of buffer which does not have a static offset. -pub struct DynamicUniform { - offsets: Vec, - cpu: encase::DynamicUniformBuffer>, - gpu: wgpu::Buffer, - label: &'static str, - size: u64, - _data: PhantomData, -} - -impl Debug for DynamicUniform { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - write!( - f, - "{:?}, {:?}, {:?}, {:?}", - self.offsets, self.gpu, self.label, self.size - ) + /// Returns the offset at `index`, if it exists. + fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { + self.offsets.get(index).expect("No offset at index.") } } -impl DynamicUniform { - pub fn new(device: &wgpu::Device, label: &'static str) -> Self { - let initial_size = u64::from(T::min_size()); - - Self { - offsets: Vec::new(), - cpu: encase::DynamicUniformBuffer::new(Vec::new()), - gpu: DynamicUniform::::create_gpu_buffer( - device, - label, - wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - initial_size, - ), - label, - size: initial_size, - _data: Default::default(), - } - } - - fn create_gpu_buffer( - device: &wgpu::Device, - label: &'static str, - usage: wgpu::BufferUsages, - size: u64, - ) -> wgpu::Buffer { - device.create_buffer(&wgpu::BufferDescriptor { - label: Some(label), - size, - usage, - mapped_at_creation: false, - }) - } - - /// Write a new value to the CPU buffer with proper alignment. Stores the returned offset value - /// in the buffer for future use. - pub fn push(&mut self, value: &T) { - //this write operation on the cpu buffer will adjust for uniform alignment requirements - let offset = self - .cpu - .write(value) - .expect("Error when writing to dynamic uniform buffer.") - as wgpu::DynamicOffset; - - self.offsets.push(offset); - } - - /// Resize buffer contents if necessary. This will re-create the GPU buffer if current size is - /// less than the newly computed size from the CPU buffer. - /// - /// If the gpu buffer is resized, its bind group will need to be recreated! - pub fn resize(&mut self, device: &wgpu::Device) -> bool { - let new_size = self.cpu.as_ref().len() as u64; - - if self.size < new_size { - self.gpu = DynamicUniform::::create_gpu_buffer( - device, - self.label, - wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, - new_size, - ); - self.size = new_size; - true - } else { - false - } - } - - /// Write the contents of this dynamic uniform buffer to the GPU via staging belt command. - pub fn write( - &mut self, - device: &wgpu::Device, - staging_belt: &mut wgpu::util::StagingBelt, - encoder: &mut wgpu::CommandEncoder, - ) { - let size = self.cpu.as_ref().len(); - - if let Some(buffer_size) = wgpu::BufferSize::new(size as u64) { - let mut buffer = staging_belt.write_buffer( - encoder, - &self.gpu, - 0, - buffer_size, - device, - ); - - buffer.copy_from_slice(self.cpu.as_ref()); - } - } - - // Gets the aligned offset at the given index from the CPU buffer. - pub fn offset_at_index(&self, index: usize) -> wgpu::DynamicOffset { - let offset = self - .offsets - .get(index) - .copied() - .expect("Index not found in offsets."); - - offset - } - - /// Returns a reference to the GPU buffer. - pub fn raw(&self) -> &wgpu::Buffer { - &self.gpu - } +fn next_copy_size(amount: usize) -> u64 { + let align_mask = wgpu::COPY_BUFFER_ALIGNMENT - 1; - /// Reset the buffer. - pub fn clear(&mut self) { - self.offsets.clear(); - self.cpu.as_mut().clear(); - self.cpu.set_offset(0); - } + (((std::mem::size_of::() * amount).next_power_of_two() as u64 + + align_mask) + & !align_mask) + .max(wgpu::COPY_BUFFER_ALIGNMENT) } diff --git a/wgpu/src/buffer/dynamic.rs b/wgpu/src/buffer/dynamic.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/wgpu/src/buffer/static.rs b/wgpu/src/buffer/static.rs deleted file mode 100644 index d8ae116e4c..0000000000 --- a/wgpu/src/buffer/static.rs +++ /dev/null @@ -1,107 +0,0 @@ -use bytemuck::{Pod, Zeroable}; -use std::marker::PhantomData; -use std::mem; - -const DEFAULT_COUNT: wgpu::BufferAddress = 128; - -/// A generic buffer struct useful for items which have no alignment requirements -/// (e.g. Vertex, Index buffers) & no dynamic offsets. -#[derive(Debug)] -pub struct Buffer { - //stored sequentially per mesh iteration; refers to the offset index in the GPU buffer - offsets: Vec, - label: &'static str, - usages: wgpu::BufferUsages, - gpu: wgpu::Buffer, - size: wgpu::BufferAddress, - _data: PhantomData, -} - -impl Buffer { - /// Initialize a new static buffer. - pub fn new( - device: &wgpu::Device, - label: &'static str, - usages: wgpu::BufferUsages, - ) -> Self { - let size = (mem::size_of::() as u64) * DEFAULT_COUNT; - - Self { - offsets: Vec::new(), - label, - usages, - gpu: Self::gpu_buffer(device, label, size, usages), - size, - _data: PhantomData, - } - } - - fn gpu_buffer( - device: &wgpu::Device, - label: &'static str, - size: wgpu::BufferAddress, - usage: wgpu::BufferUsages, - ) -> wgpu::Buffer { - device.create_buffer(&wgpu::BufferDescriptor { - label: Some(label), - size, - usage, - mapped_at_creation: false, - }) - } - - /// Returns whether or not the buffer needs to be recreated. This can happen whenever mesh data - /// changes & a redraw is requested. - pub fn resize(&mut self, device: &wgpu::Device, new_count: usize) -> bool { - let size = (mem::size_of::() * new_count) as u64; - - if self.size < size { - self.size = - (mem::size_of::() * (new_count + new_count / 2)) as u64; - - self.gpu = - Self::gpu_buffer(device, self.label, self.size, self.usages); - - self.offsets.clear(); - true - } else { - false - } - } - - /// Writes the current vertex data to the gpu buffer with a memcpy & stores its offset. - /// - /// Returns the size of the written bytes. - pub fn write( - &mut self, - queue: &wgpu::Queue, - offset: u64, - content: &[T], - ) -> u64 { - let bytes = bytemuck::cast_slice(content); - let bytes_size = bytes.len() as u64; - - queue.write_buffer(&self.gpu, offset, bytes); - self.offsets.push(offset); - - bytes_size - } - - fn offset_at(&self, index: usize) -> &wgpu::BufferAddress { - self.offsets - .get(index) - .expect("Offset at index does not exist.") - } - - /// Returns the slice calculated from the offset stored at the given index. - /// e.g. to calculate the slice for the 2nd mesh in the layer, this would be the offset at index - /// 1 that we stored earlier when writing. - pub fn slice_from_index(&self, index: usize) -> wgpu::BufferSlice<'_> { - self.gpu.slice(self.offset_at(index)..) - } - - /// Clears any temporary data from the buffer. - pub fn clear(&mut self) { - self.offsets.clear() - } -} diff --git a/wgpu/src/geometry.rs b/wgpu/src/geometry.rs index ab795ab8b6..44b1a89f57 100644 --- a/wgpu/src/geometry.rs +++ b/wgpu/src/geometry.rs @@ -1,9 +1,10 @@ -use crate::core::{Gradient, Point, Rectangle, Size, Vector}; +use crate::core::{Point, Rectangle, Size, Vector}; use crate::graphics::geometry::fill::{self, Fill}; use crate::graphics::geometry::{ LineCap, LineDash, LineJoin, Path, Stroke, Style, Text, }; use crate::graphics::primitive::{self, Primitive}; +use crate::graphics::Gradient; use lyon::geom::euclid; use lyon::tessellation; @@ -24,10 +25,7 @@ pub struct Frame { enum Buffer { Solid(tessellation::VertexBuffers), - Gradient( - tessellation::VertexBuffers, - Gradient, - ), + Gradient(tessellation::VertexBuffers), } struct BufferStack { @@ -501,7 +499,7 @@ impl tessellation::FillVertexConstructor primitive::GradientVertex2D { position: [position.x, position.y], - gradient: self.gradient.pack(), + gradient: pack_gradient(&self.gradient), } } } @@ -515,9 +513,9 @@ impl tessellation::StrokeVertexConstructor ) -> primitive::GradientVertex2D { let position = vertex.position(); - triangle::GradientVertex2D { + primitive::GradientVertex2D { position: [position.x, position.y], - gradient: self.gradient.pack(), + gradient: pack_gradient(&self.gradient), } } } @@ -620,3 +618,40 @@ pub(super) fn dashed(path: &Path, line_dash: LineDash<'_>) -> Path { ); }) } + +/// Packs the [`Gradient`] for use in shader code. +fn pack_gradient(gradient: &Gradient) -> [f32; 44] { + match gradient { + Gradient::Linear(linear) => { + let mut pack: [f32; 44] = [0.0; 44]; + let mut offsets: [f32; 8] = [2.0; 8]; + + for (index, stop) in linear.color_stops.iter().enumerate().take(8) { + let [r, g, b, a] = stop.color.into_linear(); + + pack[(index * 4)] = r; + pack[(index * 4) + 1] = g; + pack[(index * 4) + 2] = b; + pack[(index * 4) + 3] = a; + + offsets[index] = stop.offset; + } + + pack[32] = offsets[0]; + pack[33] = offsets[1]; + pack[34] = offsets[2]; + pack[35] = offsets[3]; + pack[36] = offsets[4]; + pack[37] = offsets[5]; + pack[38] = offsets[6]; + pack[39] = offsets[7]; + + pack[40] = linear.start.x; + pack[41] = linear.start.y; + pack[42] = linear.end.x; + pack[43] = linear.end.y; + + pack + } + } +} diff --git a/wgpu/src/image.rs b/wgpu/src/image.rs index 4163e3382f..b8d890911b 100644 --- a/wgpu/src/image.rs +++ b/wgpu/src/image.rs @@ -8,10 +8,10 @@ mod vector; use atlas::Atlas; +use crate::buffer::Buffer; use crate::core::{Rectangle, Size}; use crate::graphics::Transformation; -use crate::layer; -use crate::Buffer; +use crate::{layer, quad}; use std::cell::RefCell; use std::mem; @@ -121,7 +121,7 @@ impl Layer { ); let _ = self.instances.resize(device, instances.len()); - self.instances.write(queue, 0, instances); + let _ = self.instances.write(queue, 0, instances); self.instance_count = instances.len(); } @@ -131,7 +131,7 @@ impl Layer { render_pass.set_vertex_buffer(1, self.instances.slice(..)); render_pass.draw_indexed( - 0..QUAD_INDICES.len() as u32, + 0..quad::INDICES.len() as u32, 0, 0..self.instance_count as u32, ); @@ -244,22 +244,7 @@ impl Pipeline { fragment: Some(wgpu::FragmentState { module: &shader, entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], + targets: &quad::color_target_state(format), }), primitive: wgpu::PrimitiveState { topology: wgpu::PrimitiveTopology::TriangleList, @@ -278,14 +263,14 @@ impl Pipeline { let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::image vertex buffer"), - contents: bytemuck::cast_slice(&QUAD_VERTS), + contents: bytemuck::cast_slice(&quad::VERTICES), usage: wgpu::BufferUsages::VERTEX, }); let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::image index buffer"), - contents: bytemuck::cast_slice(&QUAD_INDICES), + contents: bytemuck::cast_slice(&quad::INDICES), usage: wgpu::BufferUsages::INDEX, }); @@ -498,23 +483,6 @@ pub struct Vertex { _position: [f32; 2], } -const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; - -const QUAD_VERTS: [Vertex; 4] = [ - Vertex { - _position: [0.0, 0.0], - }, - Vertex { - _position: [1.0, 0.0], - }, - Vertex { - _position: [1.0, 1.0], - }, - Vertex { - _position: [0.0, 1.0], - }, -]; - #[repr(C)] #[derive(Debug, Clone, Copy, Zeroable, Pod)] struct Instance { diff --git a/wgpu/src/layer.rs b/wgpu/src/layer.rs index 29e59ed045..f982818e80 100644 --- a/wgpu/src/layer.rs +++ b/wgpu/src/layer.rs @@ -7,6 +7,7 @@ pub mod quad; pub use image::Image; pub use mesh::Mesh; +pub use quad::{Gradient, Solid}; pub use text::Text; use crate::core::alignment; @@ -154,8 +155,7 @@ impl<'a> Layer<'a> { } => { let layer = &mut layers[current_layer]; - // TODO: Move some of these computations to the GPU (?) - let properties = Properties { + let properties = quad::Properties { position: [ bounds.x + translation.x, bounds.y + translation.y, @@ -175,10 +175,13 @@ impl<'a> Layer<'a> { } Background::Gradient(gradient) => { let quad = quad::Gradient { - gradient: (*gradient).pack(Rectangle::new( - properties.position.into(), - properties.size.into(), - )), + gradient: pack_gradient( + gradient, + Rectangle::new( + properties.position.into(), + properties.size.into(), + ), + ), properties, }; @@ -296,3 +299,34 @@ impl<'a> Layer<'a> { } } } + +/// Packs the [`Gradient`] for use in shader code. +fn pack_gradient( + gradient: &crate::core::Gradient, + bounds: Rectangle, +) -> [f32; 44] { + match gradient { + crate::core::Gradient::Linear(linear) => { + let mut pack: [f32; 44] = [0.0; 44]; + + for (index, stop) in linear.color_stops.iter().enumerate().take(8) { + let [r, g, b, a] = stop.color.into_linear(); + + pack[(index * 4)] = r; + pack[(index * 4) + 1] = g; + pack[(index * 4) + 2] = b; + pack[(index * 4) + 3] = a; + pack[32 + index] = stop.offset; + } + + let (start, end) = linear.angle.to_distance(&bounds); + + pack[40] = start.x; + pack[41] = start.y; + pack[42] = end.x; + pack[43] = end.y; + + pack + } + } +} diff --git a/wgpu/src/layer/mesh.rs b/wgpu/src/layer/mesh.rs index 20e44451fa..b7dd9a0ba4 100644 --- a/wgpu/src/layer/mesh.rs +++ b/wgpu/src/layer/mesh.rs @@ -1,5 +1,5 @@ //! A collection of triangle primitives. -use crate::core::{Gradient, Point, Rectangle}; +use crate::core::{Point, Rectangle}; use crate::graphics::primitive; /// A mesh of triangles. @@ -62,9 +62,15 @@ pub struct AttributeCount { /// The total amount of solid vertices. pub solid_vertices: usize, + /// The total amount of solid meshes. + pub solids: usize, + /// The total amount of gradient vertices. pub gradient_vertices: usize, + /// The total amount of gradient meshes. + pub gradients: usize, + /// The total amount of indices. pub indices: usize, } @@ -76,10 +82,12 @@ pub fn attribute_count_of<'a>(meshes: &'a [Mesh<'a>]) -> AttributeCount { .fold(AttributeCount::default(), |mut count, mesh| { match mesh { Mesh::Solid { buffers, .. } => { + count.solids += 1; count.solid_vertices += buffers.vertices.len(); count.indices += buffers.indices.len(); } Mesh::Gradient { buffers, .. } => { + count.gradients += 1; count.gradient_vertices += buffers.vertices.len(); count.indices += buffers.indices.len(); } diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index 473f362177..22fbb2b2b5 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -59,8 +59,6 @@ pub use backend::Backend; pub use layer::Layer; pub use settings::Settings; -use buffer::Buffer; - #[cfg(any(feature = "image", feature = "svg"))] mod image; diff --git a/wgpu/src/quad.rs b/wgpu/src/quad.rs index 04301bd60e..c6054d732b 100644 --- a/wgpu/src/quad.rs +++ b/wgpu/src/quad.rs @@ -1,16 +1,19 @@ use crate::core::Rectangle; use crate::graphics::Transformation; use crate::layer; -use crate::Buffer; +use std::mem; use wgpu::util::DeviceExt; #[cfg(feature = "tracing")] use tracing::info_span; +const INITIAL_INSTANCES: usize = 10_000; + #[derive(Debug)] pub struct Pipeline { - pipeline: wgpu::RenderPipeline, + solid: solid::Pipeline, + gradient: gradient::Pipeline, constant_layout: wgpu::BindGroupLayout, vertices: wgpu::Buffer, indices: wgpu::Buffer, @@ -37,107 +40,28 @@ impl Pipeline { }], }); - let layout = - device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { - label: Some("iced_wgpu::quad pipeline layout"), - push_constant_ranges: &[], - bind_group_layouts: &[&constant_layout], - }); - - let shader = - device.create_shader_module(wgpu::ShaderModuleDescriptor { - label: Some("iced_wgpu::quad::shader"), - source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed( - include_str!("shader/quad.wgsl"), - )), - }); - - let pipeline = - device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("iced_wgpu::quad pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "vs_main", - buffers: &[ - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &[wgpu::VertexAttribute { - shader_location: 0, - format: wgpu::VertexFormat::Float32x2, - offset: 0, - }], - }, - wgpu::VertexBufferLayout { - array_stride: mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Instance, - attributes: &wgpu::vertex_attr_array!( - 1 => Float32x2, - 2 => Float32x2, - 3 => Float32x4, - 4 => Float32x4, - 5 => Float32x4, - 6 => Float32, - ), - }, - ], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "fs_main", - targets: &[Some(wgpu::ColorTargetState { - format, - blend: Some(wgpu::BlendState { - color: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::SrcAlpha, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - alpha: wgpu::BlendComponent { - src_factor: wgpu::BlendFactor::One, - dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, - operation: wgpu::BlendOperation::Add, - }, - }), - write_mask: wgpu::ColorWrites::ALL, - })], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleList, - front_face: wgpu::FrontFace::Cw, - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState { - count: 1, - mask: !0, - alpha_to_coverage_enabled: false, - }, - multiview: None, - }); - let vertices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::quad vertex buffer"), - contents: bytemuck::cast_slice(&QUAD_VERTS), + contents: bytemuck::cast_slice(&VERTICES), usage: wgpu::BufferUsages::VERTEX, }); let indices = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { label: Some("iced_wgpu::quad index buffer"), - contents: bytemuck::cast_slice(&QUAD_INDICES), + contents: bytemuck::cast_slice(&INDICES), usage: wgpu::BufferUsages::INDEX, }); - Pipeline { - pipeline, - constant_layout, + Self { vertices, indices, + solid: solid::Pipeline::new(device, format, &constant_layout), + gradient: gradient::Pipeline::new(device, format, &constant_layout), layers: Vec::new(), prepare_layer: 0, + constant_layout, } } @@ -145,7 +69,7 @@ impl Pipeline { &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - instances: &[layer::Quad], + instances: &layer::Quads, transformation: Transformation, scale: f32, ) { @@ -166,22 +90,27 @@ impl Pipeline { render_pass: &mut wgpu::RenderPass<'a>, ) { if let Some(layer) = self.layers.get(layer) { - render_pass.set_pipeline(&self.pipeline); - render_pass.set_scissor_rect( bounds.x, bounds.y, bounds.width, bounds.height, ); - render_pass.set_index_buffer( self.indices.slice(..), wgpu::IndexFormat::Uint16, ); render_pass.set_vertex_buffer(0, self.vertices.slice(..)); - layer.draw(render_pass); + if layer.solid.instance_count > 0 { + render_pass.set_pipeline(&self.solid.pipeline); + layer.solid.draw(&layer.constants, render_pass); + } + + if layer.gradient.instance_count > 0 { + render_pass.set_pipeline(&self.gradient.pipeline); + layer.gradient.draw(&layer.constants, render_pass); + } } } @@ -194,8 +123,8 @@ impl Pipeline { struct Layer { constants: wgpu::BindGroup, constants_buffer: wgpu::Buffer, - instances: Buffer, - instance_count: usize, + solid: solid::Layer, + gradient: gradient::Layer, } impl Layer { @@ -219,18 +148,11 @@ impl Layer { }], }); - let instances = Buffer::new( - device, - "iced_wgpu::quad instance buffer", - INITIAL_INSTANCES, - wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, - ); - Self { constants, constants_buffer, - instances, - instance_count: 0, + solid: solid::Layer::new(device), + gradient: gradient::Layer::new(device), } } @@ -238,7 +160,7 @@ impl Layer { &mut self, device: &wgpu::Device, queue: &wgpu::Queue, - instances: &[layer::Quad], + instances: &layer::Quads, transformation: Transformation, scale: f32, ) { @@ -253,35 +175,351 @@ impl Layer { bytemuck::bytes_of(&uniforms), ); - let _ = self.instances.resize(device, instances.len()); - self.instances.write(queue, 0, instances); - self.instance_count = instances.len(); + let _ = self.solid.instances.resize(device, instances.solids.len()); + let _ = self + .gradient + .instances + .resize(device, instances.gradients.len()); + let _ = + self.solid + .instances + .write(queue, 0, instances.solids.as_slice()); + self.solid.instance_count = instances.solids.len(); + let _ = self.gradient.instances.write( + queue, + 0, + instances.gradients.as_slice(), + ); + self.gradient.instance_count = instances.gradients.len(); } +} - pub fn draw<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) { - #[cfg(feature = "tracing")] - let _ = info_span!("Wgpu::Quad", "DRAW").entered(); +mod solid { + use crate::buffer::Buffer; + use crate::layer::quad; + use crate::quad::{color_target_state, Vertex, INDICES, INITIAL_INSTANCES}; + use std::mem; + #[cfg(feature = "tracing")] + use tracing::info_span; + + #[derive(Debug)] + pub struct Pipeline { + pub pipeline: wgpu::RenderPipeline, + } - render_pass.set_bind_group(0, &self.constants, &[]); - render_pass.set_vertex_buffer(1, self.instances.slice(..)); + #[derive(Debug)] + pub struct Layer { + pub instances: Buffer, + pub instance_count: usize, + } - render_pass.draw_indexed( - 0..QUAD_INDICES.len() as u32, - 0, - 0..self.instance_count as u32, - ); + impl Layer { + pub fn new(device: &wgpu::Device) -> Self { + let instances = Buffer::new( + device, + "iced_wgpu::quad::solid instance buffer", + INITIAL_INSTANCES, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + Self { + instances, + instance_count: 0, + } + } + + pub fn draw<'a>( + &'a self, + constants: &'a wgpu::BindGroup, + render_pass: &mut wgpu::RenderPass<'a>, + ) { + #[cfg(feature = "tracing")] + let _ = info_span!("Wgpu::Quad::Solid", "DRAW").entered(); + + render_pass.set_bind_group(0, constants, &[]); + render_pass.set_vertex_buffer(1, self.instances.slice(..)); + + render_pass.draw_indexed( + 0..INDICES.len() as u32, + 0, + 0..self.instance_count as u32, + ); + } + } + + impl Pipeline { + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + constants_layout: &wgpu::BindGroupLayout, + ) -> Self { + let layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("iced_wgpu::quad::solid pipeline layout"), + push_constant_ranges: &[], + bind_group_layouts: &[constants_layout], + }, + ); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("iced_wgpu::quad::solid shader"), + source: wgpu::ShaderSource::Wgsl( + std::borrow::Cow::Borrowed(include_str!( + "shader/quad.wgsl" + )), + ), + }); + + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::quad::solid pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "solid_vs_main", + buffers: &[ + Vertex::buffer_layout(), + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() + as u64, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &wgpu::vertex_attr_array!( + // Color + 1 => Float32x4, + // Position + 2 => Float32x2, + // Size + 3 => Float32x2, + // Border color + 4 => Float32x4, + // Border radius + 5 => Float32x4, + // Border width + 6 => Float32, + ), + }, + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "solid_fs_main", + targets: &color_target_state(format), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }, + ); + + Self { pipeline } + } + } +} + +mod gradient { + use crate::buffer::Buffer; + use crate::layer::quad; + use crate::quad::{color_target_state, Vertex, INDICES, INITIAL_INSTANCES}; + use std::mem; + #[cfg(feature = "tracing")] + use tracing::info_span; + + #[derive(Debug)] + pub struct Pipeline { + pub pipeline: wgpu::RenderPipeline, + } + + #[derive(Debug)] + pub struct Layer { + pub instances: Buffer, + pub instance_count: usize, + } + + impl Layer { + pub fn new(device: &wgpu::Device) -> Self { + let instances = Buffer::new( + device, + "iced_wgpu::quad::gradient instances buffer", + INITIAL_INSTANCES, + wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, + ); + + Self { + instances, + instance_count: 0, + } + } + + pub fn draw<'a>( + &'a self, + constants: &'a wgpu::BindGroup, + render_pass: &mut wgpu::RenderPass<'a>, + ) { + #[cfg(feature = "tracing")] + let _ = info_span!("Wgpu::Quad::Gradient", "DRAW").entered(); + + render_pass.set_bind_group(0, constants, &[]); + render_pass.set_vertex_buffer(1, self.instances.slice(..)); + + render_pass.draw_indexed( + 0..INDICES.len() as u32, + 0, + 0..self.instance_count as u32, + ); + } + } + + impl Pipeline { + pub fn new( + device: &wgpu::Device, + format: wgpu::TextureFormat, + constants_layout: &wgpu::BindGroupLayout, + ) -> Self { + let layout = device.create_pipeline_layout( + &wgpu::PipelineLayoutDescriptor { + label: Some("iced_wgpu::quad::gradient pipeline layout"), + push_constant_ranges: &[], + bind_group_layouts: &[constants_layout], + }, + ); + + let shader = + device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("iced_wgpu::quad::gradient shader"), + source: wgpu::ShaderSource::Wgsl( + std::borrow::Cow::Borrowed(include_str!( + "shader/quad.wgsl" + )), + ), + }); + + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::quad::gradient pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "gradient_vs_main", + buffers: &[ + Vertex::buffer_layout(), + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() + as u64, + step_mode: wgpu::VertexStepMode::Instance, + attributes: &wgpu::vertex_attr_array!( + // Color 1 + 1 => Float32x4, + // Color 2 + 2 => Float32x4, + // Color 3 + 3 => Float32x4, + // Color 4 + 4 => Float32x4, + // Color 5 + 5 => Float32x4, + // Color 6 + 6 => Float32x4, + // Color 7 + 7 => Float32x4, + // Color 8 + 8 => Float32x4, + // Offsets 1-4 + 9 => Float32x4, + // Offsets 5-8 + 10 => Float32x4, + // Direction + 11 => Float32x4, + // Position & Scale + 12 => Float32x4, + // Border color + 13 => Float32x4, + // Border radius + 14 => Float32x4, + // Border width + 15 => Float32 + ), + }, + ], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "gradient_fs_main", + targets: &color_target_state(format), + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleList, + front_face: wgpu::FrontFace::Cw, + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState { + count: 1, + mask: !0, + alpha_to_coverage_enabled: false, + }, + multiview: None, + }, + ); + + Self { pipeline } + } } } +pub(crate) fn color_target_state( + format: wgpu::TextureFormat, +) -> [Option; 1] { + [Some(wgpu::ColorTargetState { + format, + blend: Some(wgpu::BlendState { + color: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::SrcAlpha, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + alpha: wgpu::BlendComponent { + src_factor: wgpu::BlendFactor::One, + dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha, + operation: wgpu::BlendOperation::Add, + }, + }), + write_mask: wgpu::ColorWrites::ALL, + })] +} + #[repr(C)] -#[derive(Clone, Copy, Zeroable, Pod)] +#[derive(Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)] pub struct Vertex { _position: [f32; 2], } -const QUAD_INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; +impl Vertex { + fn buffer_layout<'a>() -> wgpu::VertexBufferLayout<'a> { + wgpu::VertexBufferLayout { + array_stride: mem::size_of::() as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &[wgpu::VertexAttribute { + shader_location: 0, + format: wgpu::VertexFormat::Float32x2, + offset: 0, + }], + } + } +} + +pub(crate) const INDICES: [u16; 6] = [0, 1, 2, 0, 2, 3]; -const QUAD_VERTS: [Vertex; 4] = [ +pub(crate) const VERTICES: [Vertex; 4] = [ Vertex { _position: [0.0, 0.0], }, @@ -296,10 +534,8 @@ const QUAD_VERTS: [Vertex; 4] = [ }, ]; -const INITIAL_INSTANCES: usize = 10_000; - #[repr(C)] -#[derive(Debug, Clone, Copy, Zeroable, Pod)] +#[derive(Debug, Clone, Copy, bytemuck::Zeroable, bytemuck::Pod)] struct Uniforms { transform: [f32; 16], scale: f32, @@ -327,275 +563,3 @@ impl Default for Uniforms { } } } - -// mod solid { -// use crate::buffer; -// use crate::quad::{QuadVertex, INITIAL_INSTANCES}; -// use iced_graphics::layer::quad; -// use std::mem; -// -// #[derive(Debug)] -// pub struct Pipeline { -// pub pipeline: wgpu::RenderPipeline, -// pub instances: buffer::Static, -// } -// -// impl Pipeline { -// pub fn new( -// device: &wgpu::Device, -// format: wgpu::TextureFormat, -// uniforms_layout: &wgpu::BindGroupLayout, -// ) -> Self { -// let instances = buffer::Static::new( -// device, -// "iced_wgpu::quad::solid instance buffer", -// wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, -// INITIAL_INSTANCES, -// ); -// -// let layout = device.create_pipeline_layout( -// &wgpu::PipelineLayoutDescriptor { -// label: Some("iced_wgpu::quad::solid pipeline layout"), -// push_constant_ranges: &[], -// bind_group_layouts: &[uniforms_layout], -// }, -// ); -// -// let shader = -// device.create_shader_module(wgpu::ShaderModuleDescriptor { -// label: Some("iced_wgpu::quad::solid shader"), -// source: wgpu::ShaderSource::Wgsl( -// std::borrow::Cow::Borrowed(include_str!( -// "shader/quad.wgsl" -// )), -// ), -// }); -// -// let pipeline = device.create_render_pipeline( -// &wgpu::RenderPipelineDescriptor { -// label: Some("iced_wgpu::quad::solid pipeline"), -// layout: Some(&layout), -// vertex: wgpu::VertexState { -// module: &shader, -// entry_point: "solid_vs_main", -// buffers: &[ -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Vertex, -// attributes: &[wgpu::VertexAttribute { -// shader_location: 0, -// format: wgpu::VertexFormat::Float32x2, -// offset: 0, -// }], -// }, -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Instance, -// attributes: &wgpu::vertex_attr_array!( -// // Color -// 1 => Float32x4, -// // Position -// 2 => Float32x2, -// // Size -// 3 => Float32x2, -// // Border color -// 4 => Float32x4, -// // Border radius -// 5 => Float32x4, -// // Border width -// 6 => Float32, -// ), -// }, -// ], -// }, -// fragment: Some(wgpu::FragmentState { -// module: &shader, -// entry_point: "solid_fs_main", -// targets: &[Some(wgpu::ColorTargetState { -// format, -// blend: Some(wgpu::BlendState { -// color: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::SrcAlpha, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// alpha: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::One, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// }), -// write_mask: wgpu::ColorWrites::ALL, -// })], -// }), -// primitive: wgpu::PrimitiveState { -// topology: wgpu::PrimitiveTopology::TriangleList, -// front_face: wgpu::FrontFace::Cw, -// ..Default::default() -// }, -// depth_stencil: None, -// multisample: wgpu::MultisampleState { -// count: 1, -// mask: !0, -// alpha_to_coverage_enabled: false, -// }, -// multiview: None, -// }, -// ); -// -// Self { -// pipeline, -// instances, -// } -// } -// } -// } -// -// mod gradient { -// use crate::buffer; -// use crate::quad::{QuadVertex, INITIAL_INSTANCES}; -// use iced_graphics::layer::quad; -// use std::mem; -// -// #[derive(Debug)] -// pub struct Pipeline { -// pub pipeline: wgpu::RenderPipeline, -// pub instances: buffer::Static, -// } -// -// impl Pipeline { -// pub fn new( -// device: &wgpu::Device, -// format: wgpu::TextureFormat, -// uniforms_layout: &wgpu::BindGroupLayout, -// ) -> Self { -// let instances = buffer::Static::new( -// device, -// "iced_wgpu::quad::gradient instance buffer", -// wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, -// INITIAL_INSTANCES, -// ); -// -// let layout = device.create_pipeline_layout( -// &wgpu::PipelineLayoutDescriptor { -// label: Some("iced_wgpu::quad::gradient pipeline layout"), -// push_constant_ranges: &[], -// bind_group_layouts: &[uniforms_layout], -// }, -// ); -// -// let shader = -// device.create_shader_module(wgpu::ShaderModuleDescriptor { -// label: Some("iced_wgpu::quad::gradient shader"), -// source: wgpu::ShaderSource::Wgsl( -// std::borrow::Cow::Borrowed(include_str!( -// "shader/quad.wgsl" -// )), -// ), -// }); -// -// let pipeline = device.create_render_pipeline( -// &wgpu::RenderPipelineDescriptor { -// label: Some("iced_wgpu::quad::gradient pipeline"), -// layout: Some(&layout), -// vertex: wgpu::VertexState { -// module: &shader, -// entry_point: "gradient_vs_main", -// buffers: &[ -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Vertex, -// attributes: &[wgpu::VertexAttribute { -// shader_location: 0, -// format: wgpu::VertexFormat::Float32x2, -// offset: 0, -// }], -// }, -// wgpu::VertexBufferLayout { -// array_stride: mem::size_of::() -// as u64, -// step_mode: wgpu::VertexStepMode::Instance, -// attributes: &wgpu::vertex_attr_array!( -// // Color 1 -// 1 => Float32x4, -// // Color 2 -// 2 => Float32x4, -// // Color 3 -// 3 => Float32x4, -// // Color 4 -// 4 => Float32x4, -// // Color 5 -// 5 => Float32x4, -// // Color 6 -// 6 => Float32x4, -// // Color 7 -// 7 => Float32x4, -// // Color 8 -// 8 => Float32x4, -// // Offsets 1-4 -// 9 => Float32x4, -// // Offsets 5-8 -// 10 => Float32x4, -// // Direction -// 11 => Float32x4, -// // Position & Scale -// 12 => Float32x4, -// // Border color -// 13 => Float32x4, -// // Border radius -// 14 => Float32x4, -// // Border width -// 15 => Float32 -// ), -// }, -// ], -// }, -// fragment: Some(wgpu::FragmentState { -// module: &shader, -// entry_point: "gradient_fs_main", -// targets: &[Some(wgpu::ColorTargetState { -// format, -// blend: Some(wgpu::BlendState { -// color: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::SrcAlpha, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// alpha: wgpu::BlendComponent { -// src_factor: wgpu::BlendFactor::One, -// dst_factor: -// wgpu::BlendFactor::OneMinusSrcAlpha, -// operation: wgpu::BlendOperation::Add, -// }, -// }), -// write_mask: wgpu::ColorWrites::ALL, -// })], -// }), -// primitive: wgpu::PrimitiveState { -// topology: wgpu::PrimitiveTopology::TriangleList, -// front_face: wgpu::FrontFace::Cw, -// ..Default::default() -// }, -// depth_stencil: None, -// multisample: wgpu::MultisampleState { -// count: 1, -// mask: !0, -// alpha_to_coverage_enabled: false, -// }, -// multiview: None, -// }, -// ); -// -// Self { -// pipeline, -// instances, -// } -// } -// } -// } \ No newline at end of file diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 95da0c2db5..0ba375a6c4 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -1,14 +1,18 @@ //! Draw meshes of triangles. mod msaa; -use crate::buffer::r#static::Buffer; -use crate::core::{Gradient, Size}; +use crate::buffer::Buffer; +use crate::core::Size; use crate::graphics::{Antialiasing, Transformation}; use crate::layer::mesh::{self, Mesh}; +use std::mem; #[cfg(feature = "tracing")] use tracing::info_span; +const INITIAL_INDEX_COUNT: usize = 1_000; +const INITIAL_VERTEX_COUNT: usize = 1_000; + #[derive(Debug)] pub struct Pipeline { blit: Option, @@ -36,6 +40,7 @@ impl Layer { index_buffer: Buffer::new( device, "iced_wgpu::triangle index buffer", + INITIAL_INDEX_COUNT, wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST, ), index_strides: Vec::new(), @@ -49,7 +54,7 @@ impl Layer { device: &wgpu::Device, queue: &wgpu::Queue, solid: &solid::Pipeline, - #[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline, + gradient: &gradient::Pipeline, meshes: &[Mesh<'_>], transformation: Transformation, ) { @@ -62,92 +67,80 @@ impl Layer { // the majority of use cases. Therefore we will write GPU data every frame (for now). let _ = self.index_buffer.resize(device, count.indices); let _ = self.solid.vertices.resize(device, count.solid_vertices); - let _ = self.gradient.vertices.resize(device, count.gradient_vertices); + let _ = self + .gradient + .vertices + .resize(device, count.gradient_vertices); + + if self.solid.uniforms.resize(device, count.solids) { + self.solid.constants = solid::Layer::bind_group( + device, + &self.solid.uniforms.raw, + &solid.constants_layout, + ); + } + + if self.gradient.uniforms.resize(device, count.gradients) { + self.gradient.constants = gradient::Layer::bind_group( + device, + &self.gradient.uniforms.raw, + &gradient.constants_layout, + ); + } - // Prepare dynamic buffers & data store for writing - self.index_buffer.clear(); self.index_strides.clear(); self.solid.vertices.clear(); - self.gradient.vertices.clear(); self.solid.uniforms.clear(); + self.gradient.vertices.clear(); self.gradient.uniforms.clear(); let mut solid_vertex_offset = 0; + let mut solid_uniform_offset = 0; let mut gradient_vertex_offset = 0; + let mut gradient_uniform_offset = 0; let mut index_offset = 0; for mesh in meshes { let origin = mesh.origin(); let indices = mesh.indices(); - let transform = - transformation * Transformation::translate(origin.x, origin.y); + let uniforms = Uniforms::new( + transformation * Transformation::translate(origin.x, origin.y), + ); - let new_index_offset = + index_offset += self.index_buffer.write(queue, index_offset, indices); - - index_offset += new_index_offset; self.index_strides.push(indices.len() as u32); - //push uniform data to CPU buffers match mesh { Mesh::Solid { buffers, .. } => { - self.solid.uniforms.push(&solid::Uniforms::new(transform)); - - let written_bytes = self.solid.vertices.write( + solid_vertex_offset += self.solid.vertices.write( queue, solid_vertex_offset, &buffers.vertices, ); - solid_vertex_offset += written_bytes; + solid_uniform_offset += self.solid.uniforms.write( + queue, + solid_uniform_offset, + &[uniforms], + ); } Mesh::Gradient { buffers, .. } => { - self.gradient.uniforms.push(&Uniforms { - transform: transform.into(), - }); - - let written_bytes = self.gradient.vertices.write( + gradient_vertex_offset += self.gradient.vertices.write( queue, gradient_vertex_offset, &buffers.vertices, ); - gradient_vertex_offset += written_bytes; + gradient_uniform_offset += self.gradient.uniforms.write( + queue, + gradient_uniform_offset, + &[uniforms], + ); } } } - - // Write uniform data to GPU - if count.solid_vertices > 0 { - let uniforms_resized = self.solid.uniforms.resize(device); - - if uniforms_resized { - self.solid.constants = solid::Layer::bind_group( - device, - self.solid.uniforms.raw(), - &solid.constants_layout, - ) - } - - self.solid.uniforms.write(queue); - } - - if count.gradient_vertices > 0 { - // Resize buffers if needed - let uniforms_resized = self.gradient.uniforms.resize(device); - - if uniforms_resized { - self.gradient.constants = gradient::Layer::bind_group( - device, - self.gradient.uniforms.raw(), - &self.gradient.bind_group_layout, - ); - } - - // Write to GPU - self.gradient.uniforms.write(queue); - } } fn render<'a>( @@ -183,7 +176,7 @@ impl Layer { render_pass.set_bind_group( 0, &self.solid.constants, - &[self.solid.uniforms.offset_at_index(num_solids)], + &[(num_solids * mem::size_of::()) as u32], ); render_pass.set_vertex_buffer( @@ -203,10 +196,7 @@ impl Layer { render_pass.set_bind_group( 0, &self.gradient.constants, - &[self - .gradient - .uniforms - .offset_at_index(num_gradients)], + &[(num_gradients * mem::size_of::()) as u32], ); render_pass.set_vertex_buffer( @@ -214,9 +204,9 @@ impl Layer { self.gradient.vertices.slice_from_index(num_gradients), ); - num_gradients += 1; - } - }; + num_gradients += 1; + } + }; render_pass.set_index_buffer( self.index_buffer.slice_from_index(index), @@ -237,10 +227,7 @@ impl Pipeline { Pipeline { blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)), solid: solid::Pipeline::new(device, format, antialiasing), - - #[cfg(not(target_arch = "wasm32"))] gradient: gradient::Pipeline::new(device, format, antialiasing), - layers: Vec::new(), prepare_layer: 0, } @@ -257,12 +244,8 @@ impl Pipeline { let _ = info_span!("Wgpu::Triangle", "PREPARE").entered(); if self.layers.len() <= self.prepare_layer { - self.layers.push(Layer::new( - device, - &self.solid, - #[cfg(not(target_arch = "wasm32"))] - &self.gradient, - )); + self.layers + .push(Layer::new(device, &self.solid, &self.gradient)); } let layer = &mut self.layers[self.prepare_layer]; @@ -270,7 +253,6 @@ impl Pipeline { device, queue, &self.solid, - #[cfg(not(target_arch = "wasm32"))] &self.gradient, meshes, transformation, @@ -292,7 +274,6 @@ impl Pipeline { #[cfg(feature = "tracing")] let _ = info_span!("Wgpu::Triangle", "DRAW").entered(); - // Configure render pass { let (attachment, resolve_target, load) = if let Some(blit) = &mut self.blit @@ -309,9 +290,6 @@ impl Pipeline { (target, None, wgpu::LoadOp::Load) }; - #[cfg(feature = "tracing")] - let _ = info_span!("Wgpu::Triangle", "BEGIN_RENDER_PASS").enter(); - let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { label: Some("iced_wgpu::triangle render pass"), @@ -329,7 +307,6 @@ impl Pipeline { layer.render( &self.solid, - #[cfg(not(target_arch = "wasm32"))] &self.gradient, meshes, scale_factor, @@ -375,28 +352,45 @@ fn multisample_state( } } -#[derive(Debug, Clone, Copy, encase::ShaderType)] +#[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] +#[repr(C)] pub struct Uniforms { - transform: glam::Mat4, + transform: [f32; 16], + /// Uniform values must be 256-aligned; + /// see: [`wgpu::Limits`] `min_uniform_buffer_offset_alignment`. + _padding: [f32; 48], } impl Uniforms { pub fn new(transform: Transformation) -> Self { Self { transform: transform.into(), + _padding: [0.0; 48], + } + } + + pub fn entry() -> wgpu::BindGroupLayoutEntry { + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: wgpu::BufferSize::new( + mem::size_of::() as u64 + ), + }, + count: None, } } } mod solid { - use crate::buffer::dynamic; - use crate::buffer::r#static::Buffer; + use crate::buffer::Buffer; use crate::graphics::primitive; - use crate::graphics::{Antialiasing, Transformation}; + use crate::graphics::Antialiasing; use crate::triangle; - use encase::ShaderType; - #[derive(Debug)] pub struct Pipeline { pub pipeline: wgpu::RenderPipeline, @@ -406,7 +400,7 @@ mod solid { #[derive(Debug)] pub struct Layer { pub vertices: Buffer, - pub uniforms: dynamic::Buffer, + pub uniforms: Buffer, pub constants: wgpu::BindGroup, } @@ -418,16 +412,19 @@ mod solid { let vertices = Buffer::new( device, "iced_wgpu::triangle::solid vertex buffer", + triangle::INITIAL_VERTEX_COUNT, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, ); - let uniforms = dynamic::Buffer::uniform( + let uniforms = Buffer::new( device, "iced_wgpu::triangle::solid uniforms", + 1, + wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, ); let constants = - Self::bind_group(device, uniforms.raw(), constants_layout); + Self::bind_group(device, &uniforms.raw, constants_layout); Self { vertices, @@ -450,7 +447,11 @@ mod solid { wgpu::BufferBinding { buffer, offset: 0, - size: Some(Uniforms::min_size()), + size: wgpu::BufferSize::new(std::mem::size_of::< + triangle::Uniforms, + >( + ) + as u64), }, ), }], @@ -459,7 +460,6 @@ mod solid { } impl Pipeline { - /// Creates a new [SolidPipeline] using `solid.wgsl` shader. pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, @@ -468,16 +468,7 @@ mod solid { let constants_layout = device.create_bind_group_layout( &wgpu::BindGroupLayoutDescriptor { label: Some("iced_wgpu::triangle::solid bind group layout"), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }], + entries: &[triangle::Uniforms::entry()], }, ); @@ -543,12 +534,10 @@ mod solid { } mod gradient { - use crate::buffer::r#static::Buffer; - use crate::graphics::Antialiasing; + use crate::graphics::{primitive, Antialiasing}; use crate::triangle; - use glam::{IVec4, Vec4}; - use iced_graphics::primitive; + use crate::buffer::Buffer; #[derive(Debug)] pub struct Pipeline { @@ -559,7 +548,7 @@ mod gradient { #[derive(Debug)] pub struct Layer { pub vertices: Buffer, - pub uniforms: dynamic::Buffer, //TODO(shan) + pub uniforms: Buffer, pub constants: wgpu::BindGroup, } @@ -571,65 +560,55 @@ mod gradient { let vertices = Buffer::new( device, "iced_wgpu::triangle::gradient vertex buffer", + triangle::INITIAL_VERTEX_COUNT, wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST, ); - let uniforms = dynamic::Buffer::uniform( + let uniforms = Buffer::new( device, "iced_wgpu::triangle::gradient uniforms", + 1, + wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, ); - let constants = Self::bind_group( - device, - uniforms.raw(), - storage.raw(), - constants_layout, - ); + let constants = + Self::bind_group(device, &uniforms.raw, constants_layout); Self { vertices, uniforms, - storage, constants, - color_stop_offset: 0, - color_stops_pending_write: Storage { - color_stops: vec![], - }, } } pub fn bind_group( device: &wgpu::Device, uniform_buffer: &wgpu::Buffer, - storage_buffer: &wgpu::Buffer, layout: &wgpu::BindGroupLayout, ) -> wgpu::BindGroup { device.create_bind_group(&wgpu::BindGroupDescriptor { label: Some("iced_wgpu::triangle::gradient bind group"), layout, - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Buffer( - wgpu::BufferBinding { - buffer: uniform_buffer, - offset: 0, - size: Some(Uniforms::min_size()), - }, - ), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: storage_buffer.as_entire_binding(), - }, - ], + entries: &[wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Buffer( + wgpu::BufferBinding { + buffer: uniform_buffer, + offset: 0, + size: wgpu::BufferSize::new(std::mem::size_of::< + triangle::Uniforms, + >( + ) + as u64), + }, + ), + }], }) } } impl Pipeline { - /// Creates a new [GradientPipeline] using `gradient.wgsl` shader. - pub(super) fn new( + pub fn new( device: &wgpu::Device, format: wgpu::TextureFormat, antialiasing: Option, @@ -639,16 +618,7 @@ mod gradient { label: Some( "iced_wgpu::triangle::gradient bind group layout", ), - entries: &[wgpu::BindGroupLayoutEntry { - binding: 0, - visibility: wgpu::ShaderStages::VERTEX_FRAGMENT, - ty: wgpu::BindingType::Buffer { - ty: wgpu::BufferBindingType::Uniform, - has_dynamic_offset: true, - min_binding_size: Some(Uniforms::min_size()), - }, - count: None, - }], + entries: &[triangle::Uniforms::entry()], }, ); @@ -674,18 +644,20 @@ mod gradient { ), }); - let pipeline = - device.create_render_pipeline( - &wgpu::RenderPipelineDescriptor { - label: Some("iced_wgpu::triangle::gradient pipeline"), - layout: Some(&layout), - vertex: wgpu::VertexState { - module: &shader, - entry_point: "gradient_vs_main", - buffers: &[wgpu::VertexBufferLayout { - array_stride: std::mem::size_of::() as u64, - step_mode: wgpu::VertexStepMode::Vertex, - attributes: &wgpu::vertex_attr_array!( + let pipeline = device.create_render_pipeline( + &wgpu::RenderPipelineDescriptor { + label: Some("iced_wgpu::triangle::gradient pipeline"), + layout: Some(&layout), + vertex: wgpu::VertexState { + module: &shader, + entry_point: "gradient_vs_main", + buffers: &[wgpu::VertexBufferLayout { + array_stride: std::mem::size_of::< + primitive::GradientVertex2D, + >() + as u64, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &wgpu::vertex_attr_array!( // Position 0 => Float32x2, // Color 1 @@ -711,19 +683,19 @@ mod gradient { // Direction 11 => Float32x4 ), - }], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: "gradient_fs_main", - targets: &[triangle::fragment_target(format)], - }), - primitive: triangle::primitive_state(), - depth_stencil: None, - multisample: triangle::multisample_state(antialiasing), - multiview: None, + }], }, - ); + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: "gradient_fs_main", + targets: &[triangle::fragment_target(format)], + }), + primitive: triangle::primitive_state(), + depth_stencil: None, + multisample: triangle::multisample_state(antialiasing), + multiview: None, + }, + ); Self { pipeline, diff --git a/widget/src/canvas.rs b/widget/src/canvas.rs index 171c4534d8..bc969daebe 100644 --- a/widget/src/canvas.rs +++ b/widget/src/canvas.rs @@ -4,6 +4,7 @@ pub mod event; mod cursor; mod program; +pub use crate::graphics::Gradient; pub use cursor::Cursor; pub use event::Event; pub use program::Program;