wgpu: Deduplicate common gradients from a single Mesh

This commit is contained in:
Nathan Adams 2024-01-24 21:19:44 +01:00
parent e479d12af0
commit fcb74d8fbc
7 changed files with 229 additions and 167 deletions

1
Cargo.lock generated
View File

@ -4359,6 +4359,7 @@ dependencies = [
"flate2",
"gif",
"h263-rs-yuv",
"indexmap",
"jpeg-decoder",
"lru",
"lyon",

View File

@ -32,6 +32,7 @@ num-traits = "0.2"
num-derive = "0.4"
byteorder = "1.5"
wgpu = { workspace = true, optional = true }
indexmap = "2.1.0"
# This crate has a `compile_error!` on apple platforms
[target.'cfg(not(target_vendor = "apple"))'.dependencies.renderdoc]

View File

@ -12,7 +12,7 @@ pub enum FillRule {
NonZero,
}
#[derive(Copy, Clone, PartialEq, Eq, Debug, Enum)]
#[derive(Copy, Clone, PartialEq, Eq, Debug, Enum, Hash)]
pub enum GradientType {
Linear,
Radial,

View File

@ -1,5 +1,6 @@
use crate::bitmap::BitmapSource;
use crate::shape_utils::{DistilledShape, DrawCommand, DrawPath, GradientType};
use indexmap::IndexSet;
use lyon::path::Path;
use lyon::tessellation::{
self,
@ -14,6 +15,7 @@ pub struct ShapeTessellator {
fill_tess: FillTessellator,
stroke_tess: StrokeTessellator,
mesh: Vec<Draw>,
gradients: IndexSet<Gradient>,
lyon_mesh: VertexBuffers<Vertex, u32>,
mask_index_count: Option<u32>,
is_stroke: bool,
@ -25,6 +27,7 @@ impl ShapeTessellator {
fill_tess: FillTessellator::new(),
stroke_tess: StrokeTessellator::new(),
mesh: Vec::new(),
gradients: IndexSet::new(),
lyon_mesh: VertexBuffers::new(),
mask_index_count: None,
is_stroke: false,
@ -38,7 +41,9 @@ impl ShapeTessellator {
bitmap_source: &dyn BitmapSource,
) -> Mesh {
self.mesh = Vec::new();
self.gradients = IndexSet::new();
self.lyon_mesh = VertexBuffers::new();
for path in shape.paths {
let (fill_style, lyon_path, next_is_stroke) = match &path {
DrawPath::Fill {
@ -59,36 +64,49 @@ impl ShapeTessellator {
let (draw, color, needs_flush) = match fill_style {
swf::FillStyle::Color(color) => (DrawType::Color, *color, false),
swf::FillStyle::LinearGradient(gradient) => (
DrawType::Gradient(swf_gradient_to_uniforms(
GradientType::Linear,
gradient,
swf::Fixed8::ZERO,
)),
swf::Color::WHITE,
true,
),
swf::FillStyle::RadialGradient(gradient) => (
DrawType::Gradient(swf_gradient_to_uniforms(
GradientType::Radial,
gradient,
swf::Fixed8::ZERO,
)),
swf::Color::WHITE,
true,
),
swf::FillStyle::LinearGradient(gradient) => {
let uniform =
swf_gradient_to_uniforms(GradientType::Linear, gradient, swf::Fixed8::ZERO);
let (gradient_index, _) = self.gradients.insert_full(uniform);
(
DrawType::Gradient {
matrix: swf_to_gl_matrix(gradient.matrix.into()),
gradient: gradient_index,
},
swf::Color::WHITE,
true,
)
}
swf::FillStyle::RadialGradient(gradient) => {
let uniform =
swf_gradient_to_uniforms(GradientType::Radial, gradient, swf::Fixed8::ZERO);
let (gradient_index, _) = self.gradients.insert_full(uniform);
(
DrawType::Gradient {
matrix: swf_to_gl_matrix(gradient.matrix.into()),
gradient: gradient_index,
},
swf::Color::WHITE,
true,
)
}
swf::FillStyle::FocalGradient {
gradient,
focal_point,
} => (
DrawType::Gradient(swf_gradient_to_uniforms(
GradientType::Focal,
gradient,
*focal_point,
)),
swf::Color::WHITE,
true,
),
} => {
let uniform =
swf_gradient_to_uniforms(GradientType::Focal, gradient, *focal_point);
let (gradient_index, _) = self.gradients.insert_full(uniform);
(
DrawType::Gradient {
matrix: swf_to_gl_matrix(gradient.matrix.into()),
gradient: gradient_index,
},
swf::Color::WHITE,
true,
)
}
swf::FillStyle::Bitmap {
id,
matrix,
@ -197,6 +215,7 @@ impl ShapeTessellator {
self.lyon_mesh = VertexBuffers::new();
Mesh {
draws: std::mem::take(&mut self.mesh),
gradients: std::mem::take(&mut self.gradients).into_iter().collect(),
}
}
@ -228,6 +247,7 @@ impl Default for ShapeTessellator {
pub struct Mesh {
pub draws: Vec<Draw>,
pub gradients: Vec<Gradient>,
}
pub struct Draw {
@ -239,7 +259,10 @@ pub struct Draw {
pub enum DrawType {
Color,
Gradient(Gradient),
Gradient {
matrix: [[f32; 3]; 3],
gradient: usize,
},
Bitmap(Bitmap),
}
@ -253,9 +276,8 @@ impl DrawType {
}
}
#[derive(Clone, Debug)]
#[derive(Clone, Debug, Hash, Eq, PartialEq)]
pub struct Gradient {
pub matrix: [[f32; 3]; 3],
pub gradient_type: GradientType,
pub repeat_mode: swf::GradientSpread,
pub focal_point: swf::Fixed8,
@ -390,7 +412,6 @@ fn swf_gradient_to_uniforms(
focal_point: swf::Fixed8,
) -> Gradient {
Gradient {
matrix: swf_to_gl_matrix(gradient.matrix.into()),
records: gradient.records.clone(),
gradient_type,
repeat_mode: gradient.spread,

View File

@ -601,7 +601,7 @@ impl WebGlRenderBackend {
let program = match draw.draw_type {
TessDrawType::Color => &self.color_program,
TessDrawType::Gradient(_) => &self.gradient_program,
TessDrawType::Gradient { .. } => &self.gradient_program,
TessDrawType::Bitmap(_) => &self.bitmap_program,
};
@ -652,8 +652,11 @@ impl WebGlRenderBackend {
num_indices,
num_mask_indices,
},
TessDrawType::Gradient(gradient) => Draw {
draw_type: DrawType::Gradient(Box::new(Gradient::from(gradient))),
TessDrawType::Gradient { matrix, gradient } => Draw {
draw_type: DrawType::Gradient(Box::new(Gradient::new(
lyon_mesh.gradients[gradient].clone(), // TODO: Gradient deduplication
matrix,
))),
vao,
vertex_buffer: Buffer {
gl: self.gl.clone(),
@ -1529,8 +1532,8 @@ struct Gradient {
interpolation: swf::GradientInterpolation,
}
impl From<TessGradient> for Gradient {
fn from(gradient: TessGradient) -> Self {
impl Gradient {
fn new(gradient: TessGradient, matrix: [[f32; 3]; 3]) -> Self {
// TODO: Support more than MAX_GRADIENT_COLORS.
let num_colors = gradient.records.len().min(MAX_GRADIENT_COLORS);
let mut ratios = [0.0; MAX_GRADIENT_COLORS];
@ -1559,7 +1562,7 @@ impl From<TessGradient> for Gradient {
}
Self {
matrix: gradient.matrix,
matrix,
gradient_type: match gradient.gradient_type {
GradientType::Linear => 0,
GradientType::Radial => 1,

View File

@ -3,7 +3,7 @@ use crate::buffer_pool::{BufferPool, TexturePool};
use crate::context3d::WgpuContext3D;
use crate::dynamic_transforms::DynamicTransforms;
use crate::filters::FilterSource;
use crate::mesh::{Mesh, PendingDraw};
use crate::mesh::{CommonGradient, Mesh, PendingDraw};
use crate::pixel_bender::{run_pixelbender_shader_impl, ShaderMode};
use crate::surface::{LayerRef, Surface};
use crate::target::{MaybeOwnedBuffer, TextureTarget};
@ -249,6 +249,16 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
let mut uniform_buffer = BufferBuilder::new_for_uniform(&self.descriptors.limits);
let mut vertex_buffer = BufferBuilder::new_for_vertices(&self.descriptors.limits);
let mut index_buffer = BufferBuilder::new_for_vertices(&self.descriptors.limits);
let mut gradients = Vec::with_capacity(lyon_mesh.gradients.len());
for gradient in lyon_mesh.gradients {
gradients.push(CommonGradient::new(
&self.descriptors,
gradient,
&mut uniform_buffer,
));
}
for draw in lyon_mesh.draws {
let draw_id = draws.len();
if let Some(draw) = PendingDraw::new(
@ -283,7 +293,7 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
let draws = draws
.into_iter()
.map(|d| d.finish(&self.descriptors, &uniform_buffer))
.map(|d| d.finish(&self.descriptors, &uniform_buffer, &gradients))
.collect();
Mesh {

View File

@ -38,9 +38,16 @@ pub struct PendingDraw {
}
impl PendingDraw {
pub fn finish(self, descriptors: &Descriptors, uniform_buffer: &wgpu::Buffer) -> Draw {
pub fn finish(
self,
descriptors: &Descriptors,
uniform_buffer: &wgpu::Buffer,
gradients: &[CommonGradient],
) -> Draw {
Draw {
draw_type: self.draw_type.finish(descriptors, uniform_buffer),
draw_type: self
.draw_type
.finish(descriptors, uniform_buffer, gradients),
vertices: self.vertices,
indices: self.indices,
num_indices: self.num_indices,
@ -93,13 +100,9 @@ impl PendingDraw {
let index_count = draw.indices.len() as u32;
let draw_type = match draw.draw_type {
TessDrawType::Color => PendingDrawType::color(),
TessDrawType::Gradient(gradient) => PendingDrawType::gradient(
backend.descriptors(),
gradient,
shape_id,
draw_id,
uniform_buffer,
),
TessDrawType::Gradient { matrix, gradient } => {
PendingDrawType::gradient(gradient, matrix, shape_id, draw_id, uniform_buffer)
}
TessDrawType::Bitmap(bitmap) => {
PendingDrawType::bitmap(bitmap, shape_id, draw_id, source, backend, uniform_buffer)?
}
@ -120,9 +123,8 @@ pub enum PendingDrawType {
Color,
Gradient {
texture_transforms_index: wgpu::BufferAddress,
gradient: wgpu::BufferAddress,
gradient_index: usize,
bind_group_label: Option<String>,
colors: wgpu::TextureView,
},
Bitmap {
texture_transforms_index: wgpu::BufferAddress,
@ -152,13 +154,146 @@ impl PendingDrawType {
}
pub fn gradient(
descriptors: &Descriptors,
gradient: Gradient,
gradient_index: usize,
matrix: [[f32; 3]; 3],
shape_id: CharacterId,
draw_id: usize,
uniform_buffers: &mut BufferBuilder,
) -> Self {
let tex_transforms_index = create_texture_transforms(&gradient.matrix, uniform_buffers);
let tex_transforms_index = create_texture_transforms(&matrix, uniform_buffers);
let bind_group_label =
create_debug_label!("Shape {} (gradient) draw {} bindgroup", shape_id, draw_id);
PendingDrawType::Gradient {
texture_transforms_index: tex_transforms_index,
gradient_index,
bind_group_label,
}
}
pub fn bitmap(
bitmap: Bitmap,
shape_id: CharacterId,
draw_id: usize,
source: &dyn BitmapSource,
backend: &mut dyn RenderBackend,
uniform_buffers: &mut BufferBuilder,
) -> Option<Self> {
let handle = source.bitmap_handle(bitmap.bitmap_id, backend)?;
let texture = as_texture(&handle);
let texture_view = texture.texture.create_view(&Default::default());
let texture_transforms_index = create_texture_transforms(&bitmap.matrix, uniform_buffers);
let bind_group_label =
create_debug_label!("Shape {} (bitmap) draw {} bindgroup", shape_id, draw_id);
Some(PendingDrawType::Bitmap {
texture_transforms_index,
texture_view,
is_repeating: bitmap.is_repeating,
is_smoothed: bitmap.is_smoothed,
bind_group_label,
})
}
pub fn finish(
self,
descriptors: &Descriptors,
uniform_buffer: &wgpu::Buffer,
gradients: &[CommonGradient],
) -> DrawType {
match self {
PendingDrawType::Color => DrawType::Color,
PendingDrawType::Gradient {
texture_transforms_index,
gradient_index,
bind_group_label,
} => {
let common = &gradients[gradient_index];
let bind_group = descriptors
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &descriptors.bind_layouts.gradient,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: uniform_buffer,
offset: texture_transforms_index,
size: wgpu::BufferSize::new(
std::mem::size_of::<TextureTransforms>() as u64,
),
}),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: uniform_buffer,
offset: common.buffer_offset,
size: wgpu::BufferSize::new(
std::mem::size_of::<GradientUniforms>() as u64,
),
}),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&common.texture_view),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(
descriptors.bitmap_samplers.get_sampler(false, true),
),
},
],
label: bind_group_label.as_deref(),
});
DrawType::Gradient { bind_group }
}
PendingDrawType::Bitmap {
texture_transforms_index,
texture_view,
is_repeating,
is_smoothed,
bind_group_label,
} => {
let binds = BitmapBinds::new(
&descriptors.device,
&descriptors.bind_layouts.bitmap,
descriptors
.bitmap_samplers
.get_sampler(is_repeating, is_smoothed),
uniform_buffer,
texture_transforms_index,
texture_view,
bind_group_label,
);
DrawType::Bitmap { binds }
}
}
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum DrawType {
Color,
Gradient { bind_group: wgpu::BindGroup },
Bitmap { binds: BitmapBinds },
}
#[derive(Debug)]
pub struct CommonGradient {
texture_view: wgpu::TextureView,
buffer_offset: wgpu::BufferAddress,
}
impl CommonGradient {
pub fn new(
descriptors: &Descriptors,
gradient: Gradient,
uniform_buffers: &mut BufferBuilder,
) -> Self {
let colors = if gradient.records.is_empty() {
[0; GRADIENT_SIZE * 4]
} else {
@ -235,125 +370,16 @@ impl PendingDrawType {
);
let view = texture.create_view(&Default::default());
let gradient = uniform_buffers
let buffer_offset = uniform_buffers
.add(&[GradientUniforms::from(gradient)])
.expect("Mesh uniform buffer was too large!")
.start;
let bind_group_label =
create_debug_label!("Shape {} (gradient) draw {} bindgroup", shape_id, draw_id);
PendingDrawType::Gradient {
texture_transforms_index: tex_transforms_index,
gradient,
bind_group_label,
colors: view,
Self {
texture_view: view,
buffer_offset,
}
}
pub fn bitmap(
bitmap: Bitmap,
shape_id: CharacterId,
draw_id: usize,
source: &dyn BitmapSource,
backend: &mut dyn RenderBackend,
uniform_buffers: &mut BufferBuilder,
) -> Option<Self> {
let handle = source.bitmap_handle(bitmap.bitmap_id, backend)?;
let texture = as_texture(&handle);
let texture_view = texture.texture.create_view(&Default::default());
let texture_transforms_index = create_texture_transforms(&bitmap.matrix, uniform_buffers);
let bind_group_label =
create_debug_label!("Shape {} (bitmap) draw {} bindgroup", shape_id, draw_id);
Some(PendingDrawType::Bitmap {
texture_transforms_index,
texture_view,
is_repeating: bitmap.is_repeating,
is_smoothed: bitmap.is_smoothed,
bind_group_label,
})
}
pub fn finish(self, descriptors: &Descriptors, uniform_buffer: &wgpu::Buffer) -> DrawType {
match self {
PendingDrawType::Color => DrawType::Color,
PendingDrawType::Gradient {
texture_transforms_index,
gradient,
bind_group_label,
colors,
} => {
let bind_group = descriptors
.device
.create_bind_group(&wgpu::BindGroupDescriptor {
layout: &descriptors.bind_layouts.gradient,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: uniform_buffer,
offset: texture_transforms_index,
size: wgpu::BufferSize::new(
std::mem::size_of::<TextureTransforms>() as u64,
),
}),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Buffer(wgpu::BufferBinding {
buffer: uniform_buffer,
offset: gradient,
size: wgpu::BufferSize::new(
std::mem::size_of::<GradientUniforms>() as u64,
),
}),
},
wgpu::BindGroupEntry {
binding: 2,
resource: wgpu::BindingResource::TextureView(&colors),
},
wgpu::BindGroupEntry {
binding: 3,
resource: wgpu::BindingResource::Sampler(
descriptors.bitmap_samplers.get_sampler(false, true),
),
},
],
label: bind_group_label.as_deref(),
});
DrawType::Gradient { bind_group }
}
PendingDrawType::Bitmap {
texture_transforms_index,
texture_view,
is_repeating,
is_smoothed,
bind_group_label,
} => {
let binds = BitmapBinds::new(
&descriptors.device,
&descriptors.bind_layouts.bitmap,
descriptors
.bitmap_samplers
.get_sampler(is_repeating, is_smoothed),
uniform_buffer,
texture_transforms_index,
texture_view,
bind_group_label,
);
DrawType::Bitmap { binds }
}
}
}
}
#[allow(dead_code)]
#[derive(Debug)]
pub enum DrawType {
Color,
Gradient { bind_group: wgpu::BindGroup },
Bitmap { binds: BitmapBinds },
}
#[derive(Debug)]