From ea7f2626b11af249510b27001fb6addd7f9210a9 Mon Sep 17 00:00:00 2001 From: Bingus Date: Mon, 29 May 2023 16:44:56 -0700 Subject: Optimized gradient data packing. --- core/src/color.rs | 13 +++++++ graphics/src/gradient.rs | 71 +++++++++++++++++++---------------- wgpu/src/quad/gradient.rs | 34 ++++++----------- wgpu/src/shader/quad.wgsl | 87 ++++++++++++++++++++----------------------- wgpu/src/shader/triangle.wgsl | 71 ++++++++++++++++------------------- wgpu/src/triangle.rs | 26 ++++--------- 6 files changed, 144 insertions(+), 158 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 1392f28b..e3bbab73 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -145,6 +145,19 @@ impl From<[f32; 4]> for Color { } } +impl Into for Color { + fn into(self) -> u32 { + let [r, g, b, a] = self.into_rgba8(); + + let r = (r as u32) << 24; + let g = (g as u32) << 16; + let b = (b as u32) << 8; + let a = a as u32; + + r | g | b | a + } +} + /// Creates a [`Color`] with shorter and cleaner syntax. /// /// # Examples diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index d26b5665..57cc007f 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -99,61 +99,68 @@ impl Linear { /// Packs the [`Gradient`] for use in shader code. pub fn pack(&self) -> Packed { - let mut data: [f32; 44] = [0.0; 44]; + let mut colors = [0u32; 8]; + let mut offsets = [0.0f32; 8]; for (index, stop) in self.stops.iter().enumerate() { - let [r, g, b, a] = - color::pack(stop.map_or(Color::default(), |s| s.color)) - .components(); - - data[index * 4] = r; - data[(index * 4) + 1] = g; - data[(index * 4) + 2] = b; - data[(index * 4) + 3] = a; - - data[32 + index] = stop.map_or(2.0, |s| s.offset); + let (color, offset) = stop + .map_or((Color::default().into_u32(), 2.0), |s| { + (s.color.into_u32(), s.offset) + }); + colors[index] = color; + offsets[index] = offset; } - data[40] = self.start.x; - data[41] = self.start.y; - data[42] = self.end.x; - data[43] = self.end.y; + let direction = [self.start.x, self.start.y, self.end.x, self.end.y]; - Packed(data) + Packed { + colors, + offsets, + direction, + } } } /// Packed [`Gradient`] data for use in shader code. #[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] -pub struct Packed([f32; 44]); +pub struct Packed { + // 8 colors, each packed into a u32 + colors: [u32; 8], + offsets: [f32; 8], + direction: [f32; 4], +} /// Creates a new [`Packed`] gradient for use in shader code. pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed { match gradient { core::Gradient::Linear(linear) => { - let mut data: [f32; 44] = [0.0; 44]; + let mut colors = [0u32; 8]; + let mut offsets = [0.0f32; 8]; for (index, stop) in linear.stops.iter().enumerate() { - let [r, g, b, a] = - color::pack(stop.map_or(Color::default(), |s| s.color)) - .components(); - - data[index * 4] = r; - data[(index * 4) + 1] = g; - data[(index * 4) + 2] = b; - data[(index * 4) + 3] = a; - data[32 + index] = stop.map_or(2.0, |s| s.offset); + // let [r, g, b, a] = + // color::pack(stop.map_or(Color::default(), |s| s.color)) + // .components(); + + let (color, offset) = stop + .map_or((Color::default().into_u32(), 2.0), |s| { + (s.color.into_u32(), s.offset) + }); + + colors[index] = color; + offsets[index] = offset; } let (start, end) = linear.angle.to_distance(&bounds); - data[40] = start.x; - data[41] = start.y; - data[42] = end.x; - data[43] = end.y; + let direction = [start.x, start.y, end.x, end.y]; - Packed(data) + Packed { + colors, + offsets, + direction, + } } } } diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs index 2b56d594..1b45c9f4 100644 --- a/wgpu/src/quad/gradient.rs +++ b/wgpu/src/quad/gradient.rs @@ -96,36 +96,24 @@ impl Pipeline { 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, + // Colors 1-4 + 1 => Uint32x4, + // Colors 5-8 + 2 => Uint32x4, // Offsets 1-4 - 9 => Float32x4, + 3 => Float32x4, // Offsets 5-8 - 10 => Float32x4, + 4 => Float32x4, // Direction - 11 => Float32x4, + 5 => Float32x4, // Position & Scale - 12 => Float32x4, + 6 => Float32x4, // Border color - 13 => Float32x4, + 7 => Float32x4, // Border radius - 14 => Float32x4, + 8 => Float32x4, // Border width - 15 => Float32 + 9 => Float32 ), }, ], diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 3232bdbe..fdcc6743 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -38,6 +38,19 @@ fn select_border_radius(radi: vec4, position: vec2, center: vec2) return rx; } +fn l(c: f32) -> f32 { + if (c < 0.04045) { + return c / 12.92; + } else { + return pow(((c + 0.055) / 1.055), 2.4); + }; +} + +fn to_linear(color: u32) -> vec4 { + let c = unpack4x8unorm(color); //unpacks as a b g r + return vec4(l(c.w), l(c.z), l(c.y), c.x); +} + struct SolidVertexInput { @location(0) v_pos: vec2, @location(1) color: vec4, @@ -140,40 +153,28 @@ fn solid_fs_main( struct GradientVertexInput { @location(0) v_pos: vec2, - @location(1) color_1: vec4, - @location(2) color_2: vec4, - @location(3) color_3: vec4, - @location(4) color_4: vec4, - @location(5) color_5: vec4, - @location(6) color_6: vec4, - @location(7) color_7: vec4, - @location(8) color_8: vec4, - @location(9) offsets_1: vec4, - @location(10) offsets_2: vec4, - @location(11) direction: vec4, - @location(12) position_and_scale: vec4, - @location(13) border_color: vec4, - @location(14) border_radius: vec4, - @location(15) border_width: f32 + @location(1) colors_1: vec4, + @location(2) colors_2: vec4, + @location(3) offsets_1: vec4, + @location(4) offsets_2: vec4, + @location(5) direction: vec4, + @location(6) position_and_scale: vec4, + @location(7) border_color: vec4, + @location(8) border_radius: vec4, + @location(9) border_width: f32, } struct GradientVertexOutput { @builtin(position) position: vec4, - @location(1) color_1: vec4, - @location(2) color_2: vec4, - @location(3) color_3: vec4, - @location(4) color_4: vec4, - @location(5) color_5: vec4, - @location(6) color_6: vec4, - @location(7) color_7: vec4, - @location(8) color_8: vec4, - @location(9) offsets_1: vec4, - @location(10) offsets_2: vec4, - @location(11) direction: vec4, - @location(12) position_and_scale: vec4, - @location(13) border_color: vec4, - @location(14) border_radius: vec4, - @location(15) border_width: f32 + @location(1) colors_1: vec4, + @location(2) colors_2: vec4, + @location(3) offsets_1: vec4, + @location(4) offsets_2: vec4, + @location(5) direction: vec4, + @location(6) position_and_scale: vec4, + @location(7) border_color: vec4, + @location(8) border_radius: vec4, + @location(9) border_width: f32, } @vertex @@ -199,14 +200,8 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { ); out.position = globals.transform * transform * vec4(input.v_pos, 0.0, 1.0); - out.color_1 = input.color_1; - out.color_2 = input.color_2; - out.color_3 = input.color_3; - out.color_4 = input.color_4; - out.color_5 = input.color_5; - out.color_6 = input.color_6; - out.color_7 = input.color_7; - out.color_8 = input.color_8; + out.colors_1 = input.colors_1; + out.colors_2 = input.colors_2; out.offsets_1 = input.offsets_1; out.offsets_2 = input.offsets_2; out.direction = input.direction * globals.scale; @@ -274,14 +269,14 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( - input.color_1, - input.color_2, - input.color_3, - input.color_4, - input.color_5, - input.color_6, - input.color_7, - input.color_8, + to_linear(input.colors_1.x), + to_linear(input.colors_1.y), + to_linear(input.colors_1.z), + to_linear(input.colors_1.w), + to_linear(input.colors_2.x), + to_linear(input.colors_2.y), + to_linear(input.colors_2.z), + to_linear(input.colors_2.w), ); var offsets = array( diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl index 625fa46e..5a73a77f 100644 --- a/wgpu/src/shader/triangle.wgsl +++ b/wgpu/src/shader/triangle.wgsl @@ -4,6 +4,19 @@ struct Globals { @group(0) @binding(0) var globals: Globals; +fn l(c: f32) -> f32 { + if (c < 0.04045) { + return c / 12.92; + } else { + return pow(((c + 0.055) / 1.055), 2.4); + }; +} + +fn to_linear(color: u32) -> vec4 { + let c = unpack4x8unorm(color); //unpacks as a b g r + return vec4(l(c.w), l(c.z), l(c.y), c.x); +} + struct SolidVertexInput { @location(0) position: vec2, @location(1) color: vec4, @@ -32,46 +45,28 @@ fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4 { struct GradientVertexOutput { @builtin(position) position: vec4, @location(0) raw_position: vec2, - @location(1) color_1: vec4, - @location(2) color_2: vec4, - @location(3) color_3: vec4, - @location(4) color_4: vec4, - @location(5) color_5: vec4, - @location(6) color_6: vec4, - @location(7) color_7: vec4, - @location(8) color_8: vec4, - @location(9) offsets_1: vec4, - @location(10) offsets_2: vec4, - @location(11) direction: vec4, + @location(1) colors_1: vec4, + @location(2) colors_2: vec4, + @location(3) offsets_1: vec4, + @location(4) offsets_2: vec4, + @location(5) direction: vec4, } @vertex fn gradient_vs_main( @location(0) input: vec2, - @location(1) color_1: vec4, - @location(2) color_2: vec4, - @location(3) color_3: vec4, - @location(4) color_4: vec4, - @location(5) color_5: vec4, - @location(6) color_6: vec4, - @location(7) color_7: vec4, - @location(8) color_8: vec4, - @location(9) offsets_1: vec4, - @location(10) offsets_2: vec4, - @location(11) direction: vec4, + @location(1) colors_1: vec4, + @location(2) colors_2: vec4, + @location(3) offsets_1: vec4, + @location(4) offsets_2: vec4, + @location(5) direction: vec4, ) -> GradientVertexOutput { var output: GradientVertexOutput; output.position = globals.transform * vec4(input.xy, 0.0, 1.0); output.raw_position = input; - output.color_1 = color_1; - output.color_2 = color_2; - output.color_3 = color_3; - output.color_4 = color_4; - output.color_5 = color_5; - output.color_6 = color_6; - output.color_7 = color_7; - output.color_8 = color_8; + output.colors_1 = colors_1; + output.colors_2 = colors_2; output.offsets_1 = offsets_1; output.offsets_2 = offsets_2; output.direction = direction; @@ -135,14 +130,14 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( - input.color_1, - input.color_2, - input.color_3, - input.color_4, - input.color_5, - input.color_6, - input.color_7, - input.color_8, + to_linear(input.colors_1.x), + to_linear(input.colors_1.y), + to_linear(input.colors_1.z), + to_linear(input.colors_1.w), + to_linear(input.colors_2.x), + to_linear(input.colors_2.y), + to_linear(input.colors_2.z), + to_linear(input.colors_2.w), ); var offsets = array( diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 6f32f182..3f633e14 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -652,28 +652,16 @@ mod gradient { attributes: &wgpu::vertex_attr_array!( // Position 0 => Float32x2, - // Color 1 - 1 => Float32x4, - // Color 2 - 2 => Float32x4, - // Color 3 - 3 => Float32x4, - // Color 4 - 4 => Float32x4, - // Color 5 - 5 => Float32x4, - // Color 6 - 6 => Float32x4, - // Color 7 - 7 => Float32x4, - // Color 8 - 8 => Float32x4, + // Colors 1-4 + 1 => Uint32x4, + // Colors 5-8, + 2 => Uint32x4, // Offsets 1-4 - 9 => Float32x4, + 3 => Float32x4, // Offsets 5-8 - 10 => Float32x4, + 4 => Float32x4, // Direction - 11 => Float32x4 + 5 => Float32x4 ), }], }, -- cgit From 226ce3d6c96e1ee091980c3d1ba869c01920b316 Mon Sep 17 00:00:00 2001 From: Héctor Ramón Jiménez Date: Tue, 6 Jun 2023 13:37:40 +0200 Subject: Implement explicit `Color::into_u32` instead of `Into` trait --- core/src/color.rs | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index e3bbab73..9ea6ccbf 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -120,6 +120,18 @@ impl Color { ] } + /// Converts the [`Color`] into a `u32` value containing its RGBA8 components. + pub fn into_u32(self) -> u32 { + let [r, g, b, a] = self.into_rgba8(); + + let r = (r as u32) << 24; + let g = (g as u32) << 16; + let b = (b as u32) << 8; + let a = a as u32; + + r | g | b | a + } + /// Inverts the [`Color`] in-place. pub fn invert(&mut self) { self.r = 1.0f32 - self.r; @@ -145,19 +157,6 @@ impl From<[f32; 4]> for Color { } } -impl Into for Color { - fn into(self) -> u32 { - let [r, g, b, a] = self.into_rgba8(); - - let r = (r as u32) << 24; - let g = (g as u32) << 16; - let b = (b as u32) << 8; - let a = a as u32; - - r | g | b | a - } -} - /// Creates a [`Color`] with shorter and cleaner syntax. /// /// # Examples -- cgit From 9554c78f3adc9846b76e9d3b96af06e98fb69aa0 Mon Sep 17 00:00:00 2001 From: Bingus Date: Tue, 6 Jun 2023 17:06:40 -0700 Subject: Updated color packing into u32 to consider incorrect web-colors. --- core/src/color.rs | 19 +++++++++++++++++++ graphics/src/gradient.rs | 33 ++++++++++++++++++++------------- wgpu/src/shader/quad.wgsl | 29 +++++++++++------------------ wgpu/src/shader/triangle.wgsl | 29 +++++++++++------------------ 4 files changed, 61 insertions(+), 49 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 9ea6ccbf..9ef66b28 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -132,6 +132,25 @@ impl Color { r | g | b | a } + /// Converts the [`Color`] into a `u32` value containing its linear RGBA8 components. + pub fn into_linear_u32(self) -> u32 { + let [r, g, b, a] = self.into_linear(); + + let [r, g, b, a] = [ + (r * 255.0).round() as u8, + (g * 255.0).round() as u8, + (b * 255.0).round() as u8, + (a * 255.0).round() as u8, + ]; + + let r = (r as u32) << 24; + let g = (g as u32) << 16; + let b = (b as u32) << 8; + let a = a as u32; + + r | g | b | a + } + /// Inverts the [`Color`] in-place. pub fn invert(&mut self) { self.r = 1.0f32 - self.r; diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 57cc007f..97b0a6d7 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -103,11 +103,17 @@ impl Linear { let mut offsets = [0.0f32; 8]; for (index, stop) in self.stops.iter().enumerate() { - let (color, offset) = stop - .map_or((Color::default().into_u32(), 2.0), |s| { - (s.color.into_u32(), s.offset) - }); - colors[index] = color; + let (color, offset) = + stop.map_or((Color::default(), 2.0), |s| (s.color, s.offset)); + + if color::GAMMA_CORRECTION { + //correct colors, convert to linear before uploading to GPU + colors[index] = color.into_linear_u32(); + } else { + //web colors, don't convert to linear before uploading to GPU + colors[index] = color.into_u32(); + } + offsets[index] = offset; } @@ -139,16 +145,17 @@ pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed { let mut offsets = [0.0f32; 8]; for (index, stop) in linear.stops.iter().enumerate() { - // let [r, g, b, a] = - // color::pack(stop.map_or(Color::default(), |s| s.color)) - // .components(); - let (color, offset) = stop - .map_or((Color::default().into_u32(), 2.0), |s| { - (s.color.into_u32(), s.offset) - }); + .map_or((Color::default(), 2.0), |s| (s.color, s.offset)); + + if color::GAMMA_CORRECTION { + //correct colors, convert to linear before uploading to GPU + colors[index] = color.into_linear_u32(); + } else { + //web colors, don't convert to linear before uploading to GPU + colors[index] = color.into_u32(); + } - colors[index] = color; offsets[index] = offset; } diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index fdcc6743..5a1237e6 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -38,17 +38,10 @@ fn select_border_radius(radi: vec4, position: vec2, center: vec2) return rx; } -fn l(c: f32) -> f32 { - if (c < 0.04045) { - return c / 12.92; - } else { - return pow(((c + 0.055) / 1.055), 2.4); - }; -} +fn unpack_u32(color: u32) -> vec4 { + let u = unpack4x8unorm(color); -fn to_linear(color: u32) -> vec4 { - let c = unpack4x8unorm(color); //unpacks as a b g r - return vec4(l(c.w), l(c.z), l(c.y), c.x); + return vec4(u.w, u.z, u.y, u.x); } struct SolidVertexInput { @@ -269,14 +262,14 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( - to_linear(input.colors_1.x), - to_linear(input.colors_1.y), - to_linear(input.colors_1.z), - to_linear(input.colors_1.w), - to_linear(input.colors_2.x), - to_linear(input.colors_2.y), - to_linear(input.colors_2.z), - to_linear(input.colors_2.w), + unpack_u32(input.colors_1.x), + unpack_u32(input.colors_1.y), + unpack_u32(input.colors_1.z), + unpack_u32(input.colors_1.w), + unpack_u32(input.colors_2.x), + unpack_u32(input.colors_2.y), + unpack_u32(input.colors_2.z), + unpack_u32(input.colors_2.w), ); var offsets = array( diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl index 5a73a77f..f1bb2733 100644 --- a/wgpu/src/shader/triangle.wgsl +++ b/wgpu/src/shader/triangle.wgsl @@ -4,17 +4,10 @@ struct Globals { @group(0) @binding(0) var globals: Globals; -fn l(c: f32) -> f32 { - if (c < 0.04045) { - return c / 12.92; - } else { - return pow(((c + 0.055) / 1.055), 2.4); - }; -} +fn unpack_u32(color: u32) -> vec4 { + let u = unpack4x8unorm(color); -fn to_linear(color: u32) -> vec4 { - let c = unpack4x8unorm(color); //unpacks as a b g r - return vec4(l(c.w), l(c.z), l(c.y), c.x); + return vec4(u.w, u.z, u.y, u.x); } struct SolidVertexInput { @@ -130,14 +123,14 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( - to_linear(input.colors_1.x), - to_linear(input.colors_1.y), - to_linear(input.colors_1.z), - to_linear(input.colors_1.w), - to_linear(input.colors_2.x), - to_linear(input.colors_2.y), - to_linear(input.colors_2.z), - to_linear(input.colors_2.w), + unpack_u32(input.colors_1.x), + unpack_u32(input.colors_1.y), + unpack_u32(input.colors_1.z), + unpack_u32(input.colors_1.w), + unpack_u32(input.colors_2.x), + unpack_u32(input.colors_2.y), + unpack_u32(input.colors_2.z), + unpack_u32(input.colors_2.w), ); var offsets = array( -- cgit From 677f564f087b009842207e6df74aed343454ea17 Mon Sep 17 00:00:00 2001 From: Bingus Date: Wed, 7 Jun 2023 10:47:57 -0700 Subject: Switched to packing using f16s to maintain acceptable precision. --- core/src/color.rs | 31 ---------------- graphics/Cargo.toml | 1 + graphics/src/gradient.rs | 82 ++++++++++++++++++++++++++---------------- wgpu/src/quad/gradient.rs | 24 +++++++------ wgpu/src/shader/quad.wgsl | 77 +++++++++++++++++++++------------------ wgpu/src/shader/triangle.wgsl | 83 ++++++++++++++++++++++++------------------- wgpu/src/triangle.rs | 16 +++++---- 7 files changed, 163 insertions(+), 151 deletions(-) diff --git a/core/src/color.rs b/core/src/color.rs index 9ef66b28..1392f28b 100644 --- a/core/src/color.rs +++ b/core/src/color.rs @@ -120,37 +120,6 @@ impl Color { ] } - /// Converts the [`Color`] into a `u32` value containing its RGBA8 components. - pub fn into_u32(self) -> u32 { - let [r, g, b, a] = self.into_rgba8(); - - let r = (r as u32) << 24; - let g = (g as u32) << 16; - let b = (b as u32) << 8; - let a = a as u32; - - r | g | b | a - } - - /// Converts the [`Color`] into a `u32` value containing its linear RGBA8 components. - pub fn into_linear_u32(self) -> u32 { - let [r, g, b, a] = self.into_linear(); - - let [r, g, b, a] = [ - (r * 255.0).round() as u8, - (g * 255.0).round() as u8, - (b * 255.0).round() as u8, - (a * 255.0).round() as u8, - ]; - - let r = (r as u32) << 24; - let g = (g as u32) << 16; - let b = (b as u32) << 8; - let a = a as u32; - - r | g | b | a - } - /// Inverts the [`Color`] in-place. pub fn invert(&mut self) { self.r = 1.0f32 - self.r; diff --git a/graphics/Cargo.toml b/graphics/Cargo.toml index 0e22227d..02621695 100644 --- a/graphics/Cargo.toml +++ b/graphics/Cargo.toml @@ -18,6 +18,7 @@ web-colors = [] [dependencies] glam = "0.24" +half = "2.2.1" log = "0.4" raw-window-handle = "0.5" thiserror = "1.0" diff --git a/graphics/src/gradient.rs b/graphics/src/gradient.rs index 97b0a6d7..3f5d0509 100644 --- a/graphics/src/gradient.rs +++ b/graphics/src/gradient.rs @@ -7,6 +7,7 @@ use crate::color; use crate::core::gradient::ColorStop; use crate::core::{self, Color, Point, Rectangle}; +use half::f16; use std::cmp::Ordering; #[derive(Debug, Clone, PartialEq)] @@ -99,24 +100,30 @@ impl Linear { /// Packs the [`Gradient`] for use in shader code. pub fn pack(&self) -> Packed { - let mut colors = [0u32; 8]; - let mut offsets = [0.0f32; 8]; + let mut colors = [[0u32; 2]; 8]; + let mut offsets = [f16::from(0u8); 8]; for (index, stop) in self.stops.iter().enumerate() { - let (color, offset) = - stop.map_or((Color::default(), 2.0), |s| (s.color, s.offset)); - - if color::GAMMA_CORRECTION { - //correct colors, convert to linear before uploading to GPU - colors[index] = color.into_linear_u32(); - } else { - //web colors, don't convert to linear before uploading to GPU - colors[index] = color.into_u32(); - } + let [r, g, b, a] = + color::pack(stop.map_or(Color::default(), |s| s.color)) + .components(); + + colors[index] = [ + pack_f16s([f16::from_f32(r), f16::from_f32(g)]), + pack_f16s([f16::from_f32(b), f16::from_f32(a)]), + ]; - offsets[index] = offset; + offsets[index] = + stop.map_or(f16::from_f32(2.0), |s| f16::from_f32(s.offset)); } + let offsets = [ + pack_f16s([offsets[0], offsets[1]]), + pack_f16s([offsets[2], offsets[3]]), + pack_f16s([offsets[4], offsets[5]]), + pack_f16s([offsets[6], offsets[7]]), + ]; + let direction = [self.start.x, self.start.y, self.end.x, self.end.y]; Packed { @@ -131,9 +138,10 @@ impl Linear { #[derive(Debug, Copy, Clone, PartialEq)] #[repr(C)] pub struct Packed { - // 8 colors, each packed into a u32 - colors: [u32; 8], - offsets: [f32; 8], + // 8 colors, each channel = 16 bit float, 2 colors packed into 1 u32 + colors: [[u32; 2]; 8], + // 8 offsets, 8x 16 bit floats packed into 4 u32s + offsets: [u32; 4], direction: [f32; 4], } @@ -141,24 +149,30 @@ pub struct Packed { pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed { match gradient { core::Gradient::Linear(linear) => { - let mut colors = [0u32; 8]; - let mut offsets = [0.0f32; 8]; + let mut colors = [[0u32; 2]; 8]; + let mut offsets = [f16::from(0u8); 8]; for (index, stop) in linear.stops.iter().enumerate() { - let (color, offset) = stop - .map_or((Color::default(), 2.0), |s| (s.color, s.offset)); - - if color::GAMMA_CORRECTION { - //correct colors, convert to linear before uploading to GPU - colors[index] = color.into_linear_u32(); - } else { - //web colors, don't convert to linear before uploading to GPU - colors[index] = color.into_u32(); - } - - offsets[index] = offset; + let [r, g, b, a] = + color::pack(stop.map_or(Color::default(), |s| s.color)) + .components(); + + colors[index] = [ + pack_f16s([f16::from_f32(r), f16::from_f32(g)]), + pack_f16s([f16::from_f32(b), f16::from_f32(a)]), + ]; + + offsets[index] = stop + .map_or(f16::from_f32(2.0), |s| f16::from_f32(s.offset)); } + let offsets = [ + pack_f16s([offsets[0], offsets[1]]), + pack_f16s([offsets[2], offsets[3]]), + pack_f16s([offsets[4], offsets[5]]), + pack_f16s([offsets[6], offsets[7]]), + ]; + let (start, end) = linear.angle.to_distance(&bounds); let direction = [start.x, start.y, end.x, end.y]; @@ -171,3 +185,11 @@ pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed { } } } + +/// Packs two f16s into one u32. +fn pack_f16s(f: [f16; 2]) -> u32 { + let one = (f[0].to_bits() as u32) << 16; + let two = f[1].to_bits() as u32; + + one | two +} diff --git a/wgpu/src/quad/gradient.rs b/wgpu/src/quad/gradient.rs index 1b45c9f4..6db37252 100644 --- a/wgpu/src/quad/gradient.rs +++ b/wgpu/src/quad/gradient.rs @@ -96,24 +96,26 @@ impl Pipeline { as u64, step_mode: wgpu::VertexStepMode::Instance, attributes: &wgpu::vertex_attr_array!( - // Colors 1-4 + // Colors 1-2 1 => Uint32x4, - // Colors 5-8 + // Colors 3-4 2 => Uint32x4, - // Offsets 1-4 - 3 => Float32x4, - // Offsets 5-8 - 4 => Float32x4, + // Colors 5-6 + 3 => Uint32x4, + // Colors 7-8 + 4 => Uint32x4, + // Offsets 1-8 + 5 => Uint32x4, // Direction - 5 => Float32x4, - // Position & Scale 6 => Float32x4, - // Border color + // Position & Scale 7 => Float32x4, - // Border radius + // Border color 8 => Float32x4, + // Border radius + 9 => Float32x4, // Border width - 9 => Float32 + 10 => Float32 ), }, ], diff --git a/wgpu/src/shader/quad.wgsl b/wgpu/src/shader/quad.wgsl index 5a1237e6..fb402158 100644 --- a/wgpu/src/shader/quad.wgsl +++ b/wgpu/src/shader/quad.wgsl @@ -38,10 +38,11 @@ fn select_border_radius(radi: vec4, position: vec2, center: vec2) return rx; } -fn unpack_u32(color: u32) -> vec4 { - let u = unpack4x8unorm(color); +fn unpack_u32(color: vec2) -> vec4 { + let rg: vec2 = unpack2x16float(color.x); + let ba: vec2 = unpack2x16float(color.y); - return vec4(u.w, u.z, u.y, u.x); + return vec4(rg.y, rg.x, ba.y, ba.x); } struct SolidVertexInput { @@ -148,26 +149,28 @@ struct GradientVertexInput { @location(0) v_pos: vec2, @location(1) colors_1: vec4, @location(2) colors_2: vec4, - @location(3) offsets_1: vec4, - @location(4) offsets_2: vec4, - @location(5) direction: vec4, - @location(6) position_and_scale: vec4, - @location(7) border_color: vec4, - @location(8) border_radius: vec4, - @location(9) border_width: f32, + @location(3) colors_3: vec4, + @location(4) colors_4: vec4, + @location(5) offsets: vec4, + @location(6) direction: vec4, + @location(7) position_and_scale: vec4, + @location(8) border_color: vec4, + @location(9) border_radius: vec4, + @location(10) border_width: f32, } struct GradientVertexOutput { @builtin(position) position: vec4, @location(1) colors_1: vec4, @location(2) colors_2: vec4, - @location(3) offsets_1: vec4, - @location(4) offsets_2: vec4, - @location(5) direction: vec4, - @location(6) position_and_scale: vec4, - @location(7) border_color: vec4, - @location(8) border_radius: vec4, - @location(9) border_width: f32, + @location(3) colors_3: vec4, + @location(4) colors_4: vec4, + @location(5) offsets: vec4, + @location(6) direction: vec4, + @location(7) position_and_scale: vec4, + @location(8) border_color: vec4, + @location(9) border_radius: vec4, + @location(10) border_width: f32, } @vertex @@ -195,8 +198,9 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { out.position = globals.transform * transform * vec4(input.v_pos, 0.0, 1.0); out.colors_1 = input.colors_1; out.colors_2 = input.colors_2; - out.offsets_1 = input.offsets_1; - out.offsets_2 = input.offsets_2; + out.colors_3 = input.colors_3; + out.colors_4 = input.colors_4; + out.offsets = input.offsets; out.direction = input.direction * globals.scale; out.position_and_scale = vec4(pos, scale); out.border_color = input.border_color; @@ -262,25 +266,28 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( - unpack_u32(input.colors_1.x), - unpack_u32(input.colors_1.y), - unpack_u32(input.colors_1.z), - unpack_u32(input.colors_1.w), - unpack_u32(input.colors_2.x), - unpack_u32(input.colors_2.y), - unpack_u32(input.colors_2.z), - unpack_u32(input.colors_2.w), + unpack_u32(input.colors_1.xy), + unpack_u32(input.colors_1.zw), + unpack_u32(input.colors_2.xy), + unpack_u32(input.colors_2.zw), + unpack_u32(input.colors_3.xy), + unpack_u32(input.colors_3.zw), + unpack_u32(input.colors_4.xy), + unpack_u32(input.colors_4.zw), ); + let offsets_1: vec4 = unpack_u32(input.offsets.xy); + let offsets_2: vec4 = unpack_u32(input.offsets.zw); + var offsets = array( - input.offsets_1.x, - input.offsets_1.y, - input.offsets_1.z, - input.offsets_1.w, - input.offsets_2.x, - input.offsets_2.y, - input.offsets_2.z, - input.offsets_2.w, + offsets_1.x, + offsets_1.y, + offsets_1.z, + offsets_1.w, + offsets_2.x, + offsets_2.y, + offsets_2.z, + offsets_2.w, ); //TODO could just pass this in to the shader but is probably more performant to just check it here diff --git a/wgpu/src/shader/triangle.wgsl b/wgpu/src/shader/triangle.wgsl index f1bb2733..9f512d14 100644 --- a/wgpu/src/shader/triangle.wgsl +++ b/wgpu/src/shader/triangle.wgsl @@ -4,10 +4,11 @@ struct Globals { @group(0) @binding(0) var globals: Globals; -fn unpack_u32(color: u32) -> vec4 { - let u = unpack4x8unorm(color); +fn unpack_u32(color: vec2) -> vec4 { + let rg: vec2 = unpack2x16float(color.x); + let ba: vec2 = unpack2x16float(color.y); - return vec4(u.w, u.z, u.y, u.x); + return vec4(rg.y, rg.x, ba.y, ba.x); } struct SolidVertexInput { @@ -35,34 +36,39 @@ fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4 { return input.color; } +struct GradientVertexInput { + @location(0) v_pos: vec2, + @location(1) colors_1: vec4, + @location(2) colors_2: vec4, + @location(3) colors_3: vec4, + @location(4) colors_4: vec4, + @location(5) offsets: vec4, + @location(6) direction: vec4, +} + struct GradientVertexOutput { @builtin(position) position: vec4, @location(0) raw_position: vec2, @location(1) colors_1: vec4, @location(2) colors_2: vec4, - @location(3) offsets_1: vec4, - @location(4) offsets_2: vec4, - @location(5) direction: vec4, + @location(3) colors_3: vec4, + @location(4) colors_4: vec4, + @location(5) offsets: vec4, + @location(6) direction: vec4, } @vertex -fn gradient_vs_main( - @location(0) input: vec2, - @location(1) colors_1: vec4, - @location(2) colors_2: vec4, - @location(3) offsets_1: vec4, - @location(4) offsets_2: vec4, - @location(5) direction: vec4, -) -> GradientVertexOutput { +fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput { var output: GradientVertexOutput; - output.position = globals.transform * vec4(input.xy, 0.0, 1.0); - output.raw_position = input; - output.colors_1 = colors_1; - output.colors_2 = colors_2; - output.offsets_1 = offsets_1; - output.offsets_2 = offsets_2; - output.direction = direction; + output.position = globals.transform * vec4(input.v_pos, 0.0, 1.0); + output.raw_position = input.v_pos; + output.colors_1 = input.colors_1; + output.colors_2 = input.colors_2; + output.colors_3 = input.colors_3; + output.colors_4 = input.colors_4; + output.offsets = input.offsets; + output.direction = input.direction; return output; } @@ -123,25 +129,28 @@ fn gradient( @fragment fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4 { let colors = array, 8>( - unpack_u32(input.colors_1.x), - unpack_u32(input.colors_1.y), - unpack_u32(input.colors_1.z), - unpack_u32(input.colors_1.w), - unpack_u32(input.colors_2.x), - unpack_u32(input.colors_2.y), - unpack_u32(input.colors_2.z), - unpack_u32(input.colors_2.w), + unpack_u32(input.colors_1.xy), + unpack_u32(input.colors_1.zw), + unpack_u32(input.colors_2.xy), + unpack_u32(input.colors_2.zw), + unpack_u32(input.colors_3.xy), + unpack_u32(input.colors_3.zw), + unpack_u32(input.colors_4.xy), + unpack_u32(input.colors_4.zw), ); + let offsets_1: vec4 = unpack_u32(input.offsets.xy); + let offsets_2: vec4 = unpack_u32(input.offsets.zw); + var offsets = array( - input.offsets_1.x, - input.offsets_1.y, - input.offsets_1.z, - input.offsets_1.w, - input.offsets_2.x, - input.offsets_2.y, - input.offsets_2.z, - input.offsets_2.w, + offsets_1.x, + offsets_1.y, + offsets_1.z, + offsets_1.w, + offsets_2.x, + offsets_2.y, + offsets_2.z, + offsets_2.w, ); var last_index = 7; diff --git a/wgpu/src/triangle.rs b/wgpu/src/triangle.rs index 3f633e14..3f3635cf 100644 --- a/wgpu/src/triangle.rs +++ b/wgpu/src/triangle.rs @@ -652,16 +652,18 @@ mod gradient { attributes: &wgpu::vertex_attr_array!( // Position 0 => Float32x2, - // Colors 1-4 + // Colors 1-2 1 => Uint32x4, - // Colors 5-8, + // Colors 3-4 2 => Uint32x4, - // Offsets 1-4 - 3 => Float32x4, - // Offsets 5-8 - 4 => Float32x4, + // Colors 5-6 + 3 => Uint32x4, + // Colors 7-8 + 4 => Uint32x4, + // Offsets + 5 => Uint32x4, // Direction - 5 => Float32x4 + 6 => Float32x4 ), }], }, -- cgit