Reduced memory transfer of OpenGL gradient uniform upload. Rearranged gradient uniforms on OpenGL side to be more performant.

This commit is contained in:
shan 2022-10-05 16:07:43 -07:00
parent f7ce7244d0
commit 1eb8d972ba
7 changed files with 83 additions and 128 deletions

View file

@ -91,7 +91,7 @@ fn generate_box(frame: &mut Frame, bounds: Size) -> bool {
let gradient = |top_left: Point, bottom_right: Point| -> Gradient { let gradient = |top_left: Point, bottom_right: Point| -> Gradient {
let mut builder = Gradient::linear(top_left, bottom_right); let mut builder = Gradient::linear(top_left, bottom_right);
let stops = thread_rng().gen_range(1..10u32); let stops = thread_rng().gen_range(1..15u32);
let mut i = 0; let mut i = 0;
while i <= stops { while i <= stops {

View file

@ -70,11 +70,10 @@ impl Pipeline {
unsafe { unsafe {
gl.use_program(Some(program)); gl.use_program(Some(program));
let matrix: [f32; 16] = Transformation::identity().into();
gl.uniform_matrix_4_f32_slice( gl.uniform_matrix_4_f32_slice(
Some(&transform_location), Some(&transform_location),
false, false,
&matrix, Transformation::identity().as_ref(),
); );
gl.uniform_1_f32(Some(&scale_location), 1.0); gl.uniform_1_f32(Some(&scale_location), 1.0);
@ -139,11 +138,10 @@ impl Pipeline {
if transformation != self.current_transform { if transformation != self.current_transform {
unsafe { unsafe {
let matrix: [f32; 16] = transformation.into();
gl.uniform_matrix_4_f32_slice( gl.uniform_matrix_4_f32_slice(
Some(&self.transform_location), Some(&self.transform_location),
false, false,
&matrix, transformation.as_ref(),
); );
self.current_transform = transformation; self.current_transform = transformation;

View file

@ -65,11 +65,10 @@ impl Pipeline {
unsafe { unsafe {
gl.use_program(Some(program)); gl.use_program(Some(program));
let matrix: [f32; 16] = Transformation::identity().into();
gl.uniform_matrix_4_f32_slice( gl.uniform_matrix_4_f32_slice(
Some(&transform_location), Some(&transform_location),
false, false,
&matrix, Transformation::identity().as_ref(),
); );
gl.uniform_1_f32(Some(&scale_location), 1.0); gl.uniform_1_f32(Some(&scale_location), 1.0);
@ -119,11 +118,10 @@ impl Pipeline {
if transformation != self.current_transform { if transformation != self.current_transform {
unsafe { unsafe {
let matrix: [f32; 16] = transformation.into();
gl.uniform_matrix_4_f32_slice( gl.uniform_matrix_4_f32_slice(
Some(&self.transform_location), Some(&self.transform_location),
false, false,
&matrix, transformation.as_ref(),
); );
self.current_transform = transformation; self.current_transform = transformation;

View file

@ -1,6 +1,3 @@
// GLSL does not support dynamically sized arrays without SSBOs
#define MAX_STOPS 64
#ifdef GL_ES #ifdef GL_ES
#ifdef GL_FRAGMENT_PRECISION_HIGH #ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float; precision highp float;
@ -16,34 +13,38 @@ layout (location = 0) out vec4 fragColor;
in vec2 raw_position; in vec2 raw_position;
uniform vec2 gradient_start; uniform vec4 gradient_direction;
uniform vec2 gradient_end;
uniform uint color_stops_size; uniform uint color_stops_size;
uniform float color_stop_offsets[MAX_STOPS]; // GLSL does not support dynamically sized arrays without SSBOs so this is capped to 16 stops
uniform vec4 color_stop_colors[MAX_STOPS]; //stored as color(vec4) -> offset(vec4) sequentially;
uniform vec4 color_stops[32];
//TODO: rewrite without branching to make ALUs happy //TODO: rewrite without branching to make ALUs happy
void main() { void main() {
vec2 gradient_vec = vec2(gradient_end - gradient_start); vec2 start = gradient_direction.xy;
vec2 current_vec = vec2(raw_position.xy - gradient_start); vec2 end = gradient_direction.zw;
vec2 gradient_vec = vec2(end - start);
vec2 current_vec = vec2(raw_position.xy - start);
vec2 unit = normalize(gradient_vec); vec2 unit = normalize(gradient_vec);
float coord_offset = dot(unit, current_vec) / length(gradient_vec); float coord_offset = dot(unit, current_vec) / length(gradient_vec);
for (uint i = 0; i < color_stops_size - 1; i++) { for (uint i = 0; i < color_stops_size - 2; i += 2) {
float stop_offset = color_stop_offsets[i]; vec4 color = color_stops[i];
float next_stop_offset = color_stop_offsets[i + 1]; float offset = color_stops[i+1].x;
if (stop_offset <= coord_offset && coord_offset <= next_stop_offset) { vec4 next_color = color_stops[i+2];
fragColor = mix(color_stop_colors[i], color_stop_colors[i+1], smoothstep( float next_offset = color_stops[i+3].x;
stop_offset,
next_stop_offset, if (offset <= coord_offset && coord_offset <= next_offset) {
fragColor = mix(color, next_color, smoothstep(
offset,
next_offset,
coord_offset coord_offset
)); ));
} else if (coord_offset < color_stop_offsets[0]) { } else if (coord_offset < color_stops[1].x) {
fragColor = color_stop_colors[0]; fragColor = color_stops[0];
} else if (coord_offset > color_stop_offsets[color_stops_size - 1]) { } else if (coord_offset > color_stops[color_stops_size - 1].x) {
fragColor = color_stop_colors[color_stops_size - 1]; fragColor = color_stops[color_stops_size - 2];
} }
} }
} }

View file

@ -1,8 +1,8 @@
use crate::program::Version; use crate::program::Version;
use crate::triangle::{simple_triangle_program, set_transform}; use crate::triangle::{set_transform, simple_triangle_program};
use glow::{Context, HasContext, NativeProgram}; use glow::{Context, HasContext, NativeProgram};
use iced_graphics::gradient::Linear;
use iced_graphics::gradient::Gradient; use iced_graphics::gradient::Gradient;
use iced_graphics::gradient::Linear;
use iced_graphics::Transformation; use iced_graphics::Transformation;
#[derive(Debug)] #[derive(Debug)]
@ -20,21 +20,14 @@ pub struct GradientUniformData {
#[derive(Debug)] #[derive(Debug)]
struct GradientUniformLocations { struct GradientUniformLocations {
gradient_start_location: <Context as HasContext>::UniformLocation, gradient_direction_location: <Context as HasContext>::UniformLocation,
gradient_end_location: <Context as HasContext>::UniformLocation,
color_stops_size_location: <Context as HasContext>::UniformLocation, color_stops_size_location: <Context as HasContext>::UniformLocation,
//currently the maximum number of stops is 64 due to needing to allocate the //currently the maximum number of stops is 64 due to needing to allocate the
//memory for the array of stops with a const value in GLSL //memory for the array of stops with a const value in GLSL
color_stops_locations: [ColorStopLocation; 64], color_stops_location: <Context as HasContext>::UniformLocation,
transform_location: <Context as HasContext>::UniformLocation, transform_location: <Context as HasContext>::UniformLocation,
} }
#[derive(Copy, Debug, Clone)]
struct ColorStopLocation {
color: <Context as HasContext>::UniformLocation,
offset: <Context as HasContext>::UniformLocation,
}
impl GradientProgram { impl GradientProgram {
pub fn new(gl: &Context, shader_version: &Version) -> Self { pub fn new(gl: &Context, shader_version: &Version) -> Self {
let program = simple_triangle_program( let program = simple_triangle_program(
@ -56,36 +49,25 @@ impl GradientProgram {
transform: &Transformation, transform: &Transformation,
) { ) {
if transform != &self.uniform_data.transform { if transform != &self.uniform_data.transform {
set_transform(gl, self.uniform_data.uniform_locations.transform_location, *transform); set_transform(
gl,
self.uniform_data.uniform_locations.transform_location,
*transform,
);
} }
if &self.uniform_data.gradient != gradient { if &self.uniform_data.gradient != gradient {
match gradient { match gradient {
Gradient::Linear(linear) => { Gradient::Linear(linear) => {
let gradient_start: [f32; 2] = (linear.start).into();
let gradient_end: [f32; 2] = (linear.end).into();
unsafe { unsafe {
gl.uniform_2_f32( gl.uniform_4_f32(
Some( Some(
&self &self.uniform_data.uniform_locations.gradient_direction_location
.uniform_data
.uniform_locations
.gradient_start_location,
), ),
gradient_start[0], linear.start.x,
gradient_start[1], linear.start.y,
); linear.end.x,
linear.end.y
gl.uniform_2_f32(
Some(
&self
.uniform_data
.uniform_locations
.gradient_end_location,
),
gradient_end[0],
gradient_end[1],
); );
gl.uniform_1_u32( gl.uniform_1_u32(
@ -95,37 +77,32 @@ impl GradientProgram {
.uniform_locations .uniform_locations
.color_stops_size_location, .color_stops_size_location,
), ),
linear.color_stops.len() as u32, (linear.color_stops.len() * 2) as u32,
); );
for (index, stop) in let mut stops = [0.0; 128];
linear.color_stops.iter().enumerate()
{
gl.uniform_1_f32(
Some(
&self
.uniform_data
.uniform_locations
.color_stops_locations[index]
.offset,
),
stop.offset,
);
gl.uniform_4_f32( for (index, stop) in linear.color_stops.iter().enumerate() {
Some( if index == 16 { break; }
&self stops[index*8] = stop.color.r;
.uniform_data stops[(index*8)+1] = stop.color.g;
.uniform_locations stops[(index*8)+2] = stop.color.b;
.color_stops_locations[index] stops[(index*8)+3] = stop.color.a;
.color, stops[(index*8)+4] = stop.offset;
), stops[(index*8)+5] = 0.;
stop.color.r, stops[(index*8)+6] = 0.;
stop.color.g, stops[(index*8)+7] = 0.;
stop.color.b,
stop.color.a,
);
} }
gl.uniform_4_f32_slice(
Some(
&self
.uniform_data
.uniform_locations
.color_stops_location,
),
&stops,
);
} }
} }
} }
@ -134,10 +111,13 @@ impl GradientProgram {
} }
} }
pub fn use_program(&mut self, gl: &glow::Context, gradient: &Gradient, transform: &Transformation) { pub fn use_program(
unsafe { &mut self,
gl.use_program(Some(self.program)) gl: &Context,
} gradient: &Gradient,
transform: &Transformation,
) {
unsafe { gl.use_program(Some(self.program)) }
self.write_uniforms(gl, gradient, transform); self.write_uniforms(gl, gradient, transform);
} }
@ -145,38 +125,18 @@ impl GradientProgram {
impl GradientUniformData { impl GradientUniformData {
fn new(gl: &Context, program: NativeProgram) -> Self { fn new(gl: &Context, program: NativeProgram) -> Self {
let gradient_start_location = let gradient_direction_location =
unsafe { gl.get_uniform_location(program, "gradient_start") } unsafe { gl.get_uniform_location(program, "gradient_direction") }
.expect("Gradient - Get gradient_start."); .expect("Gradient - Get gradient_direction.");
let gradient_end_location =
unsafe { gl.get_uniform_location(program, "gradient_end") }
.expect("Gradient - Get gradient_end.");
let color_stops_size_location = let color_stops_size_location =
unsafe { gl.get_uniform_location(program, "color_stops_size") } unsafe { gl.get_uniform_location(program, "color_stops_size") }
.expect("Gradient - Get color_stops_size."); .expect("Gradient - Get color_stops_size.");
let color_stops_locations: [ColorStopLocation; 64] = let color_stops_location = unsafe {
core::array::from_fn(|index| { gl.get_uniform_location(program, "color_stops")
let offset = unsafe { .expect("Gradient - Get color_stops.")
gl.get_uniform_location( };
program,
&format!("color_stop_offsets[{}]", index),
)
}
.expect("Gradient - Color stop offset location.");
let color = unsafe {
gl.get_uniform_location(
program,
&format!("color_stop_colors[{}]", index),
)
}
.expect("Gradient - Color stop color location.");
ColorStopLocation { color, offset }
});
let transform_location = let transform_location =
unsafe { gl.get_uniform_location(program, "u_Transform") } unsafe { gl.get_uniform_location(program, "u_Transform") }
@ -190,10 +150,9 @@ impl GradientUniformData {
}), }),
transform: Transformation::identity(), transform: Transformation::identity(),
uniform_locations: GradientUniformLocations { uniform_locations: GradientUniformLocations {
gradient_start_location, gradient_direction_location,
gradient_end_location,
color_stops_size_location, color_stops_size_location,
color_stops_locations, color_stops_location,
transform_location, transform_location,
}, },
} }

View file

@ -28,6 +28,7 @@ pub use stroke::{LineCap, LineDash, LineJoin, Stroke};
pub use text::Text; pub use text::Text;
use crate::{Backend, Primitive, Renderer}; use crate::{Backend, Primitive, Renderer};
pub use crate::gradient::Gradient;
use iced_native::layout::{self, Layout}; use iced_native::layout::{self, Layout};
use iced_native::mouse; use iced_native::mouse;
@ -37,8 +38,6 @@ use iced_native::{
Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, Widget, Clipboard, Element, Length, Point, Rectangle, Shell, Size, Vector, Widget,
}; };
pub use crate::gradient::Gradient;
use std::marker::PhantomData; use std::marker::PhantomData;
/// A widget capable of drawing 2D graphics. /// A widget capable of drawing 2D graphics.

View file

@ -54,8 +54,8 @@ impl GradientPipeline {
"iced_wgpu::triangle [GRADIENT] uniforms", "iced_wgpu::triangle [GRADIENT] uniforms",
); );
//TODO: With a WASM target storage buffers are not supported. Will need to use UBOs & static //Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static
// sized array (64 on OpenGL side right now) to make gradients work // sized array (eg like the 64-sized array on OpenGL side right now) to make gradients work
let storage_buffer = DynamicBuffer::storage( let storage_buffer = DynamicBuffer::storage(
device, device,
"iced_wgpu::triangle [GRADIENT] storage", "iced_wgpu::triangle [GRADIENT] storage",