Refactor triangle::Pipeline into prepare and render architecture

And get rid of the staging belt! 🎉
This commit is contained in:
Héctor Ramón Jiménez 2023-02-07 23:55:16 +01:00
parent 23ed352e83
commit b8c1809ea1
No known key found for this signature in database
GPG key ID: 140CC052C94F138E
7 changed files with 358 additions and 291 deletions

View file

@ -14,50 +14,56 @@ use tracing::info_span;
#[derive(Debug)]
pub struct Pipeline {
blit: Option<msaa::Blit>,
index_buffer: Buffer<u32>,
index_strides: Vec<u32>,
solid: solid::Pipeline,
/// Gradients are currently not supported on WASM targets due to their need of storage buffers.
#[cfg(not(target_arch = "wasm32"))]
gradient: gradient::Pipeline,
layers: Vec<Layer>,
prepare_layer: usize,
render_layer: usize,
}
impl Pipeline {
pub fn new(
#[derive(Debug)]
struct Layer {
index_buffer: Buffer<u32>,
index_strides: Vec<u32>,
solid: solid::Layer,
#[cfg(not(target_arch = "wasm32"))]
gradient: gradient::Layer,
}
impl Layer {
fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Pipeline {
Pipeline {
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
solid: &solid::Pipeline,
#[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline,
) -> Self {
Self {
index_buffer: Buffer::new(
device,
"iced_wgpu::triangle vertex buffer",
wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
),
index_strides: Vec::new(),
solid: solid::Pipeline::new(device, format, antialiasing),
solid: solid::Layer::new(device, &solid.constants_layout),
#[cfg(not(target_arch = "wasm32"))]
gradient: gradient::Pipeline::new(device, format, antialiasing),
gradient: gradient::Layer::new(device, &gradient.constants_layout),
}
}
pub fn draw(
fn prepare(
&mut self,
device: &wgpu::Device,
staging_belt: &mut wgpu::util::StagingBelt,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
target_size: Size<u32>,
transformation: Transformation,
scale_factor: f32,
queue: &wgpu::Queue,
solid: &solid::Pipeline,
#[cfg(not(target_arch = "wasm32"))] gradient: &gradient::Pipeline,
meshes: &[Mesh<'_>],
transformation: Transformation,
) {
#[cfg(feature = "tracing")]
let _ = info_span!("Wgpu::Triangle", "DRAW").entered();
// Count the total amount of vertices & indices we need to handle
let count = mesh::attribute_count_of(meshes);
@ -75,6 +81,7 @@ impl Pipeline {
.resize(device, count.gradient_vertices);
// Prepare dynamic buffers & data store for writing
self.index_buffer.clear();
self.index_strides.clear();
self.solid.vertices.clear();
self.solid.uniforms.clear();
@ -99,13 +106,8 @@ impl Pipeline {
let transform =
transformation * Transformation::translate(origin.x, origin.y);
let new_index_offset = self.index_buffer.write(
device,
staging_belt,
encoder,
index_offset,
indices,
);
let new_index_offset =
self.index_buffer.write(queue, index_offset, indices);
index_offset += new_index_offset;
self.index_strides.push(indices.len() as u32);
@ -116,9 +118,7 @@ impl Pipeline {
self.solid.uniforms.push(&solid::Uniforms::new(transform));
let written_bytes = self.solid.vertices.write(
device,
staging_belt,
encoder,
queue,
solid_vertex_offset,
&buffers.vertices,
);
@ -130,9 +130,7 @@ impl Pipeline {
buffers, gradient, ..
} => {
let written_bytes = self.gradient.vertices.write(
device,
staging_belt,
encoder,
queue,
gradient_vertex_offset,
&buffers.vertices,
);
@ -196,14 +194,14 @@ impl Pipeline {
let uniforms_resized = self.solid.uniforms.resize(device);
if uniforms_resized {
self.solid.bind_group = solid::Pipeline::bind_group(
self.solid.constants = solid::Layer::bind_group(
device,
self.solid.uniforms.raw(),
&self.solid.bind_group_layout,
&solid.constants_layout,
)
}
self.solid.uniforms.write(device, staging_belt, encoder);
self.solid.uniforms.write(queue);
}
#[cfg(not(target_arch = "wasm32"))]
@ -218,22 +216,169 @@ impl Pipeline {
let storage_resized = self.gradient.storage.resize(device);
if uniforms_resized || storage_resized {
self.gradient.bind_group = gradient::Pipeline::bind_group(
self.gradient.constants = gradient::Layer::bind_group(
device,
self.gradient.uniforms.raw(),
self.gradient.storage.raw(),
&self.gradient.bind_group_layout,
&gradient.constants_layout,
);
}
// Write to GPU
self.gradient.uniforms.write(device, staging_belt, encoder);
self.gradient.storage.write(device, staging_belt, encoder);
self.gradient.uniforms.write(queue);
self.gradient.storage.write(queue);
// Cleanup
self.gradient.color_stop_offset = 0;
self.gradient.color_stops_pending_write.color_stops.clear();
}
}
fn render<'a>(
&'a mut self,
solid: &'a solid::Pipeline,
#[cfg(not(target_arch = "wasm32"))] gradient: &'a gradient::Pipeline,
meshes: &[Mesh<'_>],
scale_factor: f32,
render_pass: &mut wgpu::RenderPass<'a>,
) {
let mut num_solids = 0;
#[cfg(not(target_arch = "wasm32"))]
let mut num_gradients = 0;
let mut last_is_solid = None;
for (index, mesh) in meshes.iter().enumerate() {
let clip_bounds = (mesh.clip_bounds() * scale_factor).snap();
render_pass.set_scissor_rect(
clip_bounds.x,
clip_bounds.y,
clip_bounds.width,
clip_bounds.height,
);
match mesh {
Mesh::Solid { .. } => {
if !last_is_solid.unwrap_or(false) {
render_pass.set_pipeline(&solid.pipeline);
last_is_solid = Some(true);
}
render_pass.set_bind_group(
0,
&self.solid.constants,
&[self.solid.uniforms.offset_at_index(num_solids)],
);
render_pass.set_vertex_buffer(
0,
self.solid.vertices.slice_from_index(num_solids),
);
num_solids += 1;
}
#[cfg(not(target_arch = "wasm32"))]
Mesh::Gradient { .. } => {
if last_is_solid.unwrap_or(true) {
render_pass.set_pipeline(&gradient.pipeline);
last_is_solid = Some(false);
}
render_pass.set_bind_group(
0,
&self.gradient.constants,
&[self
.gradient
.uniforms
.offset_at_index(num_gradients)],
);
render_pass.set_vertex_buffer(
0,
self.gradient.vertices.slice_from_index(num_gradients),
);
num_gradients += 1;
}
#[cfg(target_arch = "wasm32")]
Mesh::Gradient { .. } => {}
};
render_pass.set_index_buffer(
self.index_buffer.slice_from_index(index),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..self.index_strides[index], 0, 0..1);
}
}
}
impl Pipeline {
pub fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Pipeline {
Pipeline {
blit: antialiasing.map(|a| msaa::Blit::new(device, format, a)),
solid: solid::Pipeline::new(device, format, antialiasing),
#[cfg(not(target_arch = "wasm32"))]
gradient: gradient::Pipeline::new(device, format, antialiasing),
layers: Vec::new(),
prepare_layer: 0,
render_layer: 0,
}
}
pub fn prepare(
&mut self,
device: &wgpu::Device,
queue: &wgpu::Queue,
meshes: &[Mesh<'_>],
transformation: Transformation,
) {
#[cfg(feature = "tracing")]
let _ = info_span!("Wgpu::Triangle", "PREPARE").entered();
if self.layers.len() <= self.prepare_layer {
self.layers.push(Layer::new(
device,
&self.solid,
#[cfg(not(target_arch = "wasm32"))]
&self.gradient,
));
}
let layer = &mut self.layers[self.prepare_layer];
layer.prepare(
device,
queue,
&self.solid,
#[cfg(not(target_arch = "wasm32"))]
&self.gradient,
meshes,
transformation,
);
self.prepare_layer += 1;
}
pub fn render(
&mut self,
device: &wgpu::Device,
encoder: &mut wgpu::CommandEncoder,
target: &wgpu::TextureView,
target_size: Size<u32>,
meshes: &[Mesh<'_>],
scale_factor: f32,
) {
#[cfg(feature = "tracing")]
let _ = info_span!("Wgpu::Triangle", "DRAW").entered();
// Configure render pass
{
@ -268,87 +413,29 @@ impl Pipeline {
depth_stencil_attachment: None,
});
let mut num_solids = 0;
#[cfg(not(target_arch = "wasm32"))]
let mut num_gradients = 0;
let mut last_is_solid = None;
let layer = &mut self.layers[self.render_layer];
for (index, mesh) in meshes.iter().enumerate() {
let clip_bounds = (mesh.clip_bounds() * scale_factor).snap();
render_pass.set_scissor_rect(
clip_bounds.x,
clip_bounds.y,
clip_bounds.width,
clip_bounds.height,
);
match mesh {
Mesh::Solid { .. } => {
if !last_is_solid.unwrap_or(false) {
render_pass.set_pipeline(&self.solid.pipeline);
last_is_solid = Some(true);
}
render_pass.set_bind_group(
0,
&self.solid.bind_group,
&[self.solid.uniforms.offset_at_index(num_solids)],
);
render_pass.set_vertex_buffer(
0,
self.solid.vertices.slice_from_index(num_solids),
);
num_solids += 1;
}
#[cfg(not(target_arch = "wasm32"))]
Mesh::Gradient { .. } => {
if last_is_solid.unwrap_or(true) {
render_pass.set_pipeline(&self.gradient.pipeline);
last_is_solid = Some(false);
}
render_pass.set_bind_group(
0,
&self.gradient.bind_group,
&[self
.gradient
.uniforms
.offset_at_index(num_gradients)],
);
render_pass.set_vertex_buffer(
0,
self.gradient
.vertices
.slice_from_index(num_gradients),
);
num_gradients += 1;
}
#[cfg(target_arch = "wasm32")]
Mesh::Gradient { .. } => {}
};
render_pass.set_index_buffer(
self.index_buffer.slice_from_index(index),
wgpu::IndexFormat::Uint32,
);
render_pass.draw_indexed(0..self.index_strides[index], 0, 0..1);
}
layer.render(
&self.solid,
#[cfg(not(target_arch = "wasm32"))]
&self.gradient,
meshes,
scale_factor,
&mut render_pass,
);
}
self.index_buffer.clear();
self.render_layer += 1;
if let Some(blit) = &mut self.blit {
blit.draw(encoder, target);
}
}
pub fn end_frame(&mut self) {
self.prepare_layer = 0;
self.render_layer = 0;
}
}
fn fragment_target(
@ -390,10 +477,62 @@ mod solid {
#[derive(Debug)]
pub struct Pipeline {
pub pipeline: wgpu::RenderPipeline,
pub constants_layout: wgpu::BindGroupLayout,
}
#[derive(Debug)]
pub struct Layer {
pub vertices: Buffer<triangle::ColoredVertex2D>,
pub uniforms: dynamic::Buffer<Uniforms>,
pub bind_group_layout: wgpu::BindGroupLayout,
pub bind_group: wgpu::BindGroup,
pub constants: wgpu::BindGroup,
}
impl Layer {
pub fn new(
device: &wgpu::Device,
constants_layout: &wgpu::BindGroupLayout,
) -> Self {
let vertices = Buffer::new(
device,
"iced_wgpu::triangle::solid vertex buffer",
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
);
let uniforms = dynamic::Buffer::uniform(
device,
"iced_wgpu::triangle::solid uniforms",
);
let constants =
Self::bind_group(device, uniforms.raw(), constants_layout);
Self {
vertices,
uniforms,
constants,
}
}
pub fn bind_group(
device: &wgpu::Device,
buffer: &wgpu::Buffer,
layout: &wgpu::BindGroupLayout,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("iced_wgpu::triangle::solid bind group"),
layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer,
offset: 0,
size: Some(Uniforms::min_size()),
},
),
}],
})
}
}
#[derive(Debug, Clone, Copy, ShaderType)]
@ -416,18 +555,7 @@ mod solid {
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Self {
let vertices = Buffer::new(
device,
"iced_wgpu::triangle::solid vertex buffer",
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
);
let uniforms = dynamic::Buffer::uniform(
device,
"iced_wgpu::triangle::solid uniforms",
);
let bind_group_layout = device.create_bind_group_layout(
let constants_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
label: Some("iced_wgpu::triangle::solid bind group layout"),
entries: &[wgpu::BindGroupLayoutEntry {
@ -443,13 +571,10 @@ mod solid {
},
);
let bind_group =
Self::bind_group(device, uniforms.raw(), &bind_group_layout);
let layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some("iced_wgpu::triangle::solid pipeline layout"),
bind_group_layouts: &[&bind_group_layout],
bind_group_layouts: &[&constants_layout],
push_constant_ranges: &[],
},
);
@ -501,33 +626,9 @@ mod solid {
Self {
pipeline,
vertices,
uniforms,
bind_group_layout,
bind_group,
constants_layout,
}
}
pub fn bind_group(
device: &wgpu::Device,
buffer: &wgpu::Buffer,
layout: &wgpu::BindGroupLayout,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("iced_wgpu::triangle::solid bind group"),
layout,
entries: &[wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer,
offset: 0,
size: Some(Uniforms::min_size()),
},
),
}],
})
}
}
}
@ -545,15 +646,90 @@ mod gradient {
#[derive(Debug)]
pub struct Pipeline {
pub pipeline: wgpu::RenderPipeline,
pub constants_layout: wgpu::BindGroupLayout,
}
#[derive(Debug)]
pub struct Layer {
pub vertices: Buffer<Vertex2D>,
pub uniforms: dynamic::Buffer<Uniforms>,
pub storage: dynamic::Buffer<Storage>,
pub constants: wgpu::BindGroup,
pub color_stop_offset: i32,
//Need to store these and then write them all at once
//or else they will be padded to 256 and cause gaps in the storage buffer
pub color_stops_pending_write: Storage,
pub bind_group_layout: wgpu::BindGroupLayout,
pub bind_group: wgpu::BindGroup,
}
impl Layer {
pub fn new(
device: &wgpu::Device,
constants_layout: &wgpu::BindGroupLayout,
) -> Self {
let vertices = Buffer::new(
device,
"iced_wgpu::triangle::gradient vertex buffer",
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
);
let uniforms = dynamic::Buffer::uniform(
device,
"iced_wgpu::triangle::gradient uniforms",
);
// Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static
// sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work
let storage = dynamic::Buffer::storage(
device,
"iced_wgpu::triangle::gradient storage",
);
let constants = Self::bind_group(
device,
uniforms.raw(),
storage.raw(),
constants_layout,
);
Self {
vertices,
uniforms,
storage,
constants,
color_stop_offset: 0,
color_stops_pending_write: Storage {
color_stops: vec![],
},
}
}
pub fn bind_group(
device: &wgpu::Device,
uniform_buffer: &wgpu::Buffer,
storage_buffer: &wgpu::Buffer,
layout: &wgpu::BindGroupLayout,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("iced_wgpu::triangle::gradient bind group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: uniform_buffer,
offset: 0,
size: Some(Uniforms::min_size()),
},
),
},
wgpu::BindGroupEntry {
binding: 1,
resource: storage_buffer.as_entire_binding(),
},
],
})
}
}
#[derive(Debug, ShaderType)]
@ -584,25 +760,7 @@ mod gradient {
format: wgpu::TextureFormat,
antialiasing: Option<settings::Antialiasing>,
) -> Self {
let vertices = Buffer::new(
device,
"iced_wgpu::triangle::gradient vertex buffer",
wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
);
let uniforms = dynamic::Buffer::uniform(
device,
"iced_wgpu::triangle::gradient uniforms",
);
//Note: with a WASM target storage buffers are not supported. Will need to use UBOs & static
// sized array (eg like the 32-sized array on OpenGL side right now) to make gradients work
let storage = dynamic::Buffer::storage(
device,
"iced_wgpu::triangle::gradient storage",
);
let bind_group_layout = device.create_bind_group_layout(
let constants_layout = device.create_bind_group_layout(
&wgpu::BindGroupLayoutDescriptor {
label: Some(
"iced_wgpu::triangle::gradient bind group layout",
@ -634,19 +792,12 @@ mod gradient {
},
);
let bind_group = Pipeline::bind_group(
device,
uniforms.raw(),
storage.raw(),
&bind_group_layout,
);
let layout = device.create_pipeline_layout(
&wgpu::PipelineLayoutDescriptor {
label: Some(
"iced_wgpu::triangle::gradient pipeline layout",
),
bind_group_layouts: &[&bind_group_layout],
bind_group_layouts: &[&constants_layout],
push_constant_ranges: &[],
},
);
@ -694,44 +845,8 @@ mod gradient {
Self {
pipeline,
vertices,
uniforms,
storage,
color_stop_offset: 0,
color_stops_pending_write: Storage {
color_stops: vec![],
},
bind_group_layout,
bind_group,
constants_layout,
}
}
pub fn bind_group(
device: &wgpu::Device,
uniform_buffer: &wgpu::Buffer,
storage_buffer: &wgpu::Buffer,
layout: &wgpu::BindGroupLayout,
) -> wgpu::BindGroup {
device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("iced_wgpu::triangle::gradient bind group"),
layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::Buffer(
wgpu::BufferBinding {
buffer: uniform_buffer,
offset: 0,
size: Some(Uniforms::min_size()),
},
),
},
wgpu::BindGroupEntry {
binding: 1,
resource: storage_buffer.as_entire_binding(),
},
],
})
}
}
}