Merge pull request #1885 from bungoboingo/gradient-packing-optimization

Small gradient optimization
This commit is contained in:
Héctor Ramón 2023-06-27 22:10:17 +02:00 committed by GitHub
commit c7332c1522
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 188 additions and 179 deletions

View file

@ -18,6 +18,7 @@ web-colors = []
[dependencies] [dependencies]
glam = "0.24" glam = "0.24"
half = "2.2.1"
log = "0.4" log = "0.4"
raw-window-handle = "0.5" raw-window-handle = "0.5"
thiserror = "1.0" thiserror = "1.0"

View file

@ -7,6 +7,7 @@ use crate::color;
use crate::core::gradient::ColorStop; use crate::core::gradient::ColorStop;
use crate::core::{self, Color, Point, Rectangle}; use crate::core::{self, Color, Point, Rectangle};
use half::f16;
use std::cmp::Ordering; use std::cmp::Ordering;
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@ -99,61 +100,96 @@ impl Linear {
/// Packs the [`Gradient`] for use in shader code. /// Packs the [`Gradient`] for use in shader code.
pub fn pack(&self) -> Packed { pub fn pack(&self) -> Packed {
let mut data: [f32; 44] = [0.0; 44]; let mut colors = [[0u32; 2]; 8];
let mut offsets = [f16::from(0u8); 8];
for (index, stop) in self.stops.iter().enumerate() { for (index, stop) in self.stops.iter().enumerate() {
let [r, g, b, a] = let [r, g, b, a] =
color::pack(stop.map_or(Color::default(), |s| s.color)) color::pack(stop.map_or(Color::default(), |s| s.color))
.components(); .components();
data[index * 4] = r; colors[index] = [
data[(index * 4) + 1] = g; pack_f16s([f16::from_f32(r), f16::from_f32(g)]),
data[(index * 4) + 2] = b; pack_f16s([f16::from_f32(b), f16::from_f32(a)]),
data[(index * 4) + 3] = a; ];
data[32 + index] = stop.map_or(2.0, |s| s.offset); offsets[index] =
stop.map_or(f16::from_f32(2.0), |s| f16::from_f32(s.offset));
} }
data[40] = self.start.x; let offsets = [
data[41] = self.start.y; pack_f16s([offsets[0], offsets[1]]),
data[42] = self.end.x; pack_f16s([offsets[2], offsets[3]]),
data[43] = self.end.y; pack_f16s([offsets[4], offsets[5]]),
pack_f16s([offsets[6], offsets[7]]),
];
Packed(data) let direction = [self.start.x, self.start.y, self.end.x, self.end.y];
Packed {
colors,
offsets,
direction,
}
} }
} }
/// Packed [`Gradient`] data for use in shader code. /// Packed [`Gradient`] data for use in shader code.
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
#[repr(C)] #[repr(C)]
pub struct Packed([f32; 44]); pub struct Packed {
// 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],
}
/// Creates a new [`Packed`] gradient for use in shader code. /// Creates a new [`Packed`] gradient for use in shader code.
pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed { pub fn pack(gradient: &core::Gradient, bounds: Rectangle) -> Packed {
match gradient { match gradient {
core::Gradient::Linear(linear) => { core::Gradient::Linear(linear) => {
let mut data: [f32; 44] = [0.0; 44]; let mut colors = [[0u32; 2]; 8];
let mut offsets = [f16::from(0u8); 8];
for (index, stop) in linear.stops.iter().enumerate() { for (index, stop) in linear.stops.iter().enumerate() {
let [r, g, b, a] = let [r, g, b, a] =
color::pack(stop.map_or(Color::default(), |s| s.color)) color::pack(stop.map_or(Color::default(), |s| s.color))
.components(); .components();
data[index * 4] = r; colors[index] = [
data[(index * 4) + 1] = g; pack_f16s([f16::from_f32(r), f16::from_f32(g)]),
data[(index * 4) + 2] = b; pack_f16s([f16::from_f32(b), f16::from_f32(a)]),
data[(index * 4) + 3] = a; ];
data[32 + index] = stop.map_or(2.0, |s| s.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 (start, end) = linear.angle.to_distance(&bounds); let (start, end) = linear.angle.to_distance(&bounds);
data[40] = start.x; let direction = [start.x, start.y, end.x, end.y];
data[41] = start.y;
data[42] = end.x;
data[43] = end.y;
Packed(data) Packed {
colors,
offsets,
direction,
}
} }
} }
} }
/// 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
}

View file

@ -96,36 +96,26 @@ impl Pipeline {
as u64, as u64,
step_mode: wgpu::VertexStepMode::Instance, step_mode: wgpu::VertexStepMode::Instance,
attributes: &wgpu::vertex_attr_array!( attributes: &wgpu::vertex_attr_array!(
// Color 1 // Colors 1-2
1 => Float32x4, 1 => Uint32x4,
// Color 2 // Colors 3-4
2 => Float32x4, 2 => Uint32x4,
// Color 3 // Colors 5-6
3 => Float32x4, 3 => Uint32x4,
// Color 4 // Colors 7-8
4 => Float32x4, 4 => Uint32x4,
// Color 5 // Offsets 1-8
5 => Float32x4, 5 => Uint32x4,
// Color 6
6 => Float32x4,
// Color 7
7 => Float32x4,
// Color 8
8 => Float32x4,
// Offsets 1-4
9 => Float32x4,
// Offsets 5-8
10 => Float32x4,
// Direction // Direction
11 => Float32x4, 6 => Float32x4,
// Position & Scale // Position & Scale
12 => Float32x4, 7 => Float32x4,
// Border color // Border color
13 => Float32x4, 8 => Float32x4,
// Border radius // Border radius
14 => Float32x4, 9 => Float32x4,
// Border width // Border width
15 => Float32 10 => Float32
), ),
}, },
], ],

View file

@ -38,6 +38,13 @@ fn select_border_radius(radi: vec4<f32>, position: vec2<f32>, center: vec2<f32>)
return rx; return rx;
} }
fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
let rg: vec2<f32> = unpack2x16float(color.x);
let ba: vec2<f32> = unpack2x16float(color.y);
return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
}
struct SolidVertexInput { struct SolidVertexInput {
@location(0) v_pos: vec2<f32>, @location(0) v_pos: vec2<f32>,
@location(1) color: vec4<f32>, @location(1) color: vec4<f32>,
@ -140,40 +147,30 @@ fn solid_fs_main(
struct GradientVertexInput { struct GradientVertexInput {
@location(0) v_pos: vec2<f32>, @location(0) v_pos: vec2<f32>,
@location(1) color_1: vec4<f32>, @location(1) colors_1: vec4<u32>,
@location(2) color_2: vec4<f32>, @location(2) colors_2: vec4<u32>,
@location(3) color_3: vec4<f32>, @location(3) colors_3: vec4<u32>,
@location(4) color_4: vec4<f32>, @location(4) colors_4: vec4<u32>,
@location(5) color_5: vec4<f32>, @location(5) offsets: vec4<u32>,
@location(6) color_6: vec4<f32>, @location(6) direction: vec4<f32>,
@location(7) color_7: vec4<f32>, @location(7) position_and_scale: vec4<f32>,
@location(8) color_8: vec4<f32>, @location(8) border_color: vec4<f32>,
@location(9) offsets_1: vec4<f32>, @location(9) border_radius: vec4<f32>,
@location(10) offsets_2: vec4<f32>, @location(10) border_width: f32,
@location(11) direction: vec4<f32>,
@location(12) position_and_scale: vec4<f32>,
@location(13) border_color: vec4<f32>,
@location(14) border_radius: vec4<f32>,
@location(15) border_width: f32
} }
struct GradientVertexOutput { struct GradientVertexOutput {
@builtin(position) position: vec4<f32>, @builtin(position) position: vec4<f32>,
@location(1) color_1: vec4<f32>, @location(1) colors_1: vec4<u32>,
@location(2) color_2: vec4<f32>, @location(2) colors_2: vec4<u32>,
@location(3) color_3: vec4<f32>, @location(3) colors_3: vec4<u32>,
@location(4) color_4: vec4<f32>, @location(4) colors_4: vec4<u32>,
@location(5) color_5: vec4<f32>, @location(5) offsets: vec4<u32>,
@location(6) color_6: vec4<f32>, @location(6) direction: vec4<f32>,
@location(7) color_7: vec4<f32>, @location(7) position_and_scale: vec4<f32>,
@location(8) color_8: vec4<f32>, @location(8) border_color: vec4<f32>,
@location(9) offsets_1: vec4<f32>, @location(9) border_radius: vec4<f32>,
@location(10) offsets_2: vec4<f32>, @location(10) border_width: f32,
@location(11) direction: vec4<f32>,
@location(12) position_and_scale: vec4<f32>,
@location(13) border_color: vec4<f32>,
@location(14) border_radius: vec4<f32>,
@location(15) border_width: f32
} }
@vertex @vertex
@ -199,16 +196,11 @@ fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
); );
out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0); out.position = globals.transform * transform * vec4<f32>(input.v_pos, 0.0, 1.0);
out.color_1 = input.color_1; out.colors_1 = input.colors_1;
out.color_2 = input.color_2; out.colors_2 = input.colors_2;
out.color_3 = input.color_3; out.colors_3 = input.colors_3;
out.color_4 = input.color_4; out.colors_4 = input.colors_4;
out.color_5 = input.color_5; out.offsets = input.offsets;
out.color_6 = input.color_6;
out.color_7 = input.color_7;
out.color_8 = input.color_8;
out.offsets_1 = input.offsets_1;
out.offsets_2 = input.offsets_2;
out.direction = input.direction * globals.scale; out.direction = input.direction * globals.scale;
out.position_and_scale = vec4<f32>(pos, scale); out.position_and_scale = vec4<f32>(pos, scale);
out.border_color = input.border_color; out.border_color = input.border_color;
@ -274,25 +266,28 @@ fn gradient(
@fragment @fragment
fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> { fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
let colors = array<vec4<f32>, 8>( let colors = array<vec4<f32>, 8>(
input.color_1, unpack_u32(input.colors_1.xy),
input.color_2, unpack_u32(input.colors_1.zw),
input.color_3, unpack_u32(input.colors_2.xy),
input.color_4, unpack_u32(input.colors_2.zw),
input.color_5, unpack_u32(input.colors_3.xy),
input.color_6, unpack_u32(input.colors_3.zw),
input.color_7, unpack_u32(input.colors_4.xy),
input.color_8, unpack_u32(input.colors_4.zw),
); );
let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
var offsets = array<f32, 8>( var offsets = array<f32, 8>(
input.offsets_1.x, offsets_1.x,
input.offsets_1.y, offsets_1.y,
input.offsets_1.z, offsets_1.z,
input.offsets_1.w, offsets_1.w,
input.offsets_2.x, offsets_2.x,
input.offsets_2.y, offsets_2.y,
input.offsets_2.z, offsets_2.z,
input.offsets_2.w, offsets_2.w,
); );
//TODO could just pass this in to the shader but is probably more performant to just check it here //TODO could just pass this in to the shader but is probably more performant to just check it here

View file

@ -4,6 +4,13 @@ struct Globals {
@group(0) @binding(0) var<uniform> globals: Globals; @group(0) @binding(0) var<uniform> globals: Globals;
fn unpack_u32(color: vec2<u32>) -> vec4<f32> {
let rg: vec2<f32> = unpack2x16float(color.x);
let ba: vec2<f32> = unpack2x16float(color.y);
return vec4<f32>(rg.y, rg.x, ba.y, ba.x);
}
struct SolidVertexInput { struct SolidVertexInput {
@location(0) position: vec2<f32>, @location(0) position: vec2<f32>,
@location(1) color: vec4<f32>, @location(1) color: vec4<f32>,
@ -29,52 +36,39 @@ fn solid_fs_main(input: SolidVertexOutput) -> @location(0) vec4<f32> {
return input.color; return input.color;
} }
struct GradientVertexInput {
@location(0) v_pos: vec2<f32>,
@location(1) colors_1: vec4<u32>,
@location(2) colors_2: vec4<u32>,
@location(3) colors_3: vec4<u32>,
@location(4) colors_4: vec4<u32>,
@location(5) offsets: vec4<u32>,
@location(6) direction: vec4<f32>,
}
struct GradientVertexOutput { struct GradientVertexOutput {
@builtin(position) position: vec4<f32>, @builtin(position) position: vec4<f32>,
@location(0) raw_position: vec2<f32>, @location(0) raw_position: vec2<f32>,
@location(1) color_1: vec4<f32>, @location(1) colors_1: vec4<u32>,
@location(2) color_2: vec4<f32>, @location(2) colors_2: vec4<u32>,
@location(3) color_3: vec4<f32>, @location(3) colors_3: vec4<u32>,
@location(4) color_4: vec4<f32>, @location(4) colors_4: vec4<u32>,
@location(5) color_5: vec4<f32>, @location(5) offsets: vec4<u32>,
@location(6) color_6: vec4<f32>, @location(6) direction: vec4<f32>,
@location(7) color_7: vec4<f32>,
@location(8) color_8: vec4<f32>,
@location(9) offsets_1: vec4<f32>,
@location(10) offsets_2: vec4<f32>,
@location(11) direction: vec4<f32>,
} }
@vertex @vertex
fn gradient_vs_main( fn gradient_vs_main(input: GradientVertexInput) -> GradientVertexOutput {
@location(0) input: vec2<f32>,
@location(1) color_1: vec4<f32>,
@location(2) color_2: vec4<f32>,
@location(3) color_3: vec4<f32>,
@location(4) color_4: vec4<f32>,
@location(5) color_5: vec4<f32>,
@location(6) color_6: vec4<f32>,
@location(7) color_7: vec4<f32>,
@location(8) color_8: vec4<f32>,
@location(9) offsets_1: vec4<f32>,
@location(10) offsets_2: vec4<f32>,
@location(11) direction: vec4<f32>,
) -> GradientVertexOutput {
var output: GradientVertexOutput; var output: GradientVertexOutput;
output.position = globals.transform * vec4<f32>(input.xy, 0.0, 1.0); output.position = globals.transform * vec4<f32>(input.v_pos, 0.0, 1.0);
output.raw_position = input; output.raw_position = input.v_pos;
output.color_1 = color_1; output.colors_1 = input.colors_1;
output.color_2 = color_2; output.colors_2 = input.colors_2;
output.color_3 = color_3; output.colors_3 = input.colors_3;
output.color_4 = color_4; output.colors_4 = input.colors_4;
output.color_5 = color_5; output.offsets = input.offsets;
output.color_6 = color_6; output.direction = input.direction;
output.color_7 = color_7;
output.color_8 = color_8;
output.offsets_1 = offsets_1;
output.offsets_2 = offsets_2;
output.direction = direction;
return output; return output;
} }
@ -135,25 +129,28 @@ fn gradient(
@fragment @fragment
fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> { fn gradient_fs_main(input: GradientVertexOutput) -> @location(0) vec4<f32> {
let colors = array<vec4<f32>, 8>( let colors = array<vec4<f32>, 8>(
input.color_1, unpack_u32(input.colors_1.xy),
input.color_2, unpack_u32(input.colors_1.zw),
input.color_3, unpack_u32(input.colors_2.xy),
input.color_4, unpack_u32(input.colors_2.zw),
input.color_5, unpack_u32(input.colors_3.xy),
input.color_6, unpack_u32(input.colors_3.zw),
input.color_7, unpack_u32(input.colors_4.xy),
input.color_8, unpack_u32(input.colors_4.zw),
); );
let offsets_1: vec4<f32> = unpack_u32(input.offsets.xy);
let offsets_2: vec4<f32> = unpack_u32(input.offsets.zw);
var offsets = array<f32, 8>( var offsets = array<f32, 8>(
input.offsets_1.x, offsets_1.x,
input.offsets_1.y, offsets_1.y,
input.offsets_1.z, offsets_1.z,
input.offsets_1.w, offsets_1.w,
input.offsets_2.x, offsets_2.x,
input.offsets_2.y, offsets_2.y,
input.offsets_2.z, offsets_2.z,
input.offsets_2.w, offsets_2.w,
); );
var last_index = 7; var last_index = 7;

View file

@ -652,28 +652,18 @@ mod gradient {
attributes: &wgpu::vertex_attr_array!( attributes: &wgpu::vertex_attr_array!(
// Position // Position
0 => Float32x2, 0 => Float32x2,
// Color 1 // Colors 1-2
1 => Float32x4, 1 => Uint32x4,
// Color 2 // Colors 3-4
2 => Float32x4, 2 => Uint32x4,
// Color 3 // Colors 5-6
3 => Float32x4, 3 => Uint32x4,
// Color 4 // Colors 7-8
4 => Float32x4, 4 => Uint32x4,
// Color 5 // Offsets
5 => Float32x4, 5 => Uint32x4,
// Color 6
6 => Float32x4,
// Color 7
7 => Float32x4,
// Color 8
8 => Float32x4,
// Offsets 1-4
9 => Float32x4,
// Offsets 5-8
10 => Float32x4,
// Direction // Direction
11 => Float32x4 6 => Float32x4
), ),
}], }],
}, },