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. --- graphics/src/gradient.rs | 71 ++++++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 32 deletions(-) (limited to 'graphics') 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, + } } } } -- 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. --- graphics/src/gradient.rs | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'graphics') 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; } -- 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. --- graphics/Cargo.toml | 1 + graphics/src/gradient.rs | 82 ++++++++++++++++++++++++++++++------------------ 2 files changed, 53 insertions(+), 30 deletions(-) (limited to 'graphics') 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 +} -- cgit