webgl/wgpu: Omit strokes when drawing a mask stencil

Adjust `common_tess` to add an additional `mask_index_count` to
draws. This is used to not render strokes when drawing a shape as
a mask stencil.

Fixes #7027.
This commit is contained in:
Mike Welsh 2022-05-24 16:19:59 -07:00
parent b39d54de2c
commit d3c64d4eb7
3 changed files with 63 additions and 10 deletions

View File

@ -13,6 +13,8 @@ pub struct ShapeTessellator {
stroke_tess: StrokeTessellator, stroke_tess: StrokeTessellator,
mesh: Vec<Draw>, mesh: Vec<Draw>,
lyon_mesh: VertexBuffers<Vertex, u32>, lyon_mesh: VertexBuffers<Vertex, u32>,
mask_index_count: Option<u32>,
is_stroke: bool,
} }
impl ShapeTessellator { impl ShapeTessellator {
@ -22,6 +24,8 @@ impl ShapeTessellator {
stroke_tess: StrokeTessellator::new(), stroke_tess: StrokeTessellator::new(),
mesh: Vec::new(), mesh: Vec::new(),
lyon_mesh: VertexBuffers::new(), lyon_mesh: VertexBuffers::new(),
mask_index_count: None,
is_stroke: false,
} }
} }
@ -33,9 +37,9 @@ impl ShapeTessellator {
self.mesh = Vec::new(); self.mesh = Vec::new();
self.lyon_mesh = VertexBuffers::new(); self.lyon_mesh = VertexBuffers::new();
for path in shape.paths { for path in shape.paths {
let (fill_style, lyon_path) = match &path { let (fill_style, lyon_path, next_is_stroke) = match &path {
DrawPath::Fill { style, commands } => { DrawPath::Fill { style, commands } => {
(*style, ruffle_path_to_lyon_path(commands, true)) (*style, ruffle_path_to_lyon_path(commands, true), false)
} }
DrawPath::Stroke { DrawPath::Stroke {
style, style,
@ -44,6 +48,7 @@ impl ShapeTessellator {
} => ( } => (
style.fill_style(), style.fill_style(),
ruffle_path_to_lyon_path(&commands, *is_closed), ruffle_path_to_lyon_path(&commands, *is_closed),
true,
), ),
}; };
@ -107,10 +112,19 @@ impl ShapeTessellator {
} }
}; };
if needs_flush { if needs_flush || (self.is_stroke && !next_is_stroke) {
// Non-solid color fills are isolated draw calls, so flush any pending color fill. // We flush separate draw calls in these cases:
// * Non-solid color fills which require their own shader.
// * Strokes followed by fills, because strokes need to be omitted
// when using this shape as a mask.
self.flush_draw(DrawType::Color); self.flush_draw(DrawType::Color);
} else if !self.is_stroke && next_is_stroke {
// Bake solid color fills followed by strokes into a single draw call, and adjust
// the index count to omit the strokes when rendering this shape as a mask.
debug_assert!(self.mask_index_count.is_none());
self.mask_index_count = Some(self.lyon_mesh.indices.len() as u32);
} }
self.is_stroke = next_is_stroke;
let mut buffers_builder = let mut buffers_builder =
BuffersBuilder::new(&mut self.lyon_mesh, RuffleVertexCtor { color }); BuffersBuilder::new(&mut self.lyon_mesh, RuffleVertexCtor { color });
@ -187,9 +201,13 @@ impl ShapeTessellator {
let draw_mesh = std::mem::replace(&mut self.lyon_mesh, VertexBuffers::new()); let draw_mesh = std::mem::replace(&mut self.lyon_mesh, VertexBuffers::new());
self.mesh.push(Draw { self.mesh.push(Draw {
draw_type: draw, draw_type: draw,
mask_index_count: self
.mask_index_count
.unwrap_or(draw_mesh.indices.len() as u32),
vertices: draw_mesh.vertices, vertices: draw_mesh.vertices,
indices: draw_mesh.indices, indices: draw_mesh.indices,
}); });
self.mask_index_count = None;
} }
} }
@ -205,6 +223,7 @@ pub struct Draw {
pub draw_type: DrawType, pub draw_type: DrawType,
pub vertices: Vec<Vertex>, pub vertices: Vec<Vertex>,
pub indices: Vec<u32>, pub indices: Vec<u32>,
pub mask_index_count: u32,
} }
pub enum DrawType { pub enum DrawType {

View File

@ -348,6 +348,7 @@ impl WebGlRenderBackend {
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
num_indices: 6, num_indices: 6,
num_mask_indices: 6,
}], }],
}; };
Ok(quad_mesh) Ok(quad_mesh)
@ -501,6 +502,7 @@ impl WebGlRenderBackend {
let mut draws = Vec::with_capacity(lyon_mesh.len()); let mut draws = Vec::with_capacity(lyon_mesh.len());
for draw in lyon_mesh { for draw in lyon_mesh {
let num_indices = draw.indices.len() as i32; let num_indices = draw.indices.len() as i32;
let num_mask_indices = draw.mask_index_count as i32;
let vao = self.create_vertex_array().unwrap(); let vao = self.create_vertex_array().unwrap();
let vertex_buffer = self.gl.create_buffer().unwrap(); let vertex_buffer = self.gl.create_buffer().unwrap();
@ -565,6 +567,7 @@ impl WebGlRenderBackend {
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
num_indices, num_indices,
num_mask_indices,
}, },
TessDrawType::Gradient(gradient) => Draw { TessDrawType::Gradient(gradient) => Draw {
draw_type: DrawType::Gradient(Box::new(Gradient::from(gradient))), draw_type: DrawType::Gradient(Box::new(Gradient::from(gradient))),
@ -572,6 +575,7 @@ impl WebGlRenderBackend {
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
num_indices, num_indices,
num_mask_indices,
}, },
TessDrawType::Bitmap(bitmap) => Draw { TessDrawType::Bitmap(bitmap) => Draw {
draw_type: DrawType::Bitmap(BitmapDraw { draw_type: DrawType::Bitmap(BitmapDraw {
@ -584,6 +588,7 @@ impl WebGlRenderBackend {
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
num_indices, num_indices,
num_mask_indices,
}, },
}); });
@ -947,6 +952,18 @@ impl RenderBackend for WebGlRenderBackend {
let mesh = &self.meshes[shape.0]; let mesh = &self.meshes[shape.0];
for draw in &mesh.draws { for draw in &mesh.draws {
// Ignore strokes when drawing a mask stencil.
let num_indices = if self.mask_state != MaskState::DrawMaskStencil
&& self.mask_state != MaskState::ClearMaskStencil
{
draw.num_indices
} else {
draw.num_mask_indices
};
if num_indices == 0 {
continue;
}
self.bind_vertex_array(Some(&draw.vao)); self.bind_vertex_array(Some(&draw.vao));
let (program, src_blend, dst_blend) = match &draw.draw_type { let (program, src_blend, dst_blend) = match &draw.draw_type {
@ -1068,7 +1085,7 @@ impl RenderBackend for WebGlRenderBackend {
// Draw the triangles. // Draw the triangles.
self.gl self.gl
.draw_elements_with_i32(Gl::TRIANGLES, draw.num_indices, Gl::UNSIGNED_INT, 0); .draw_elements_with_i32(Gl::TRIANGLES, num_indices, Gl::UNSIGNED_INT, 0);
} }
} }
@ -1336,6 +1353,7 @@ struct Draw {
index_buffer: WebGlBuffer, index_buffer: WebGlBuffer,
vao: WebGlVertexArrayObject, vao: WebGlVertexArrayObject,
num_indices: i32, num_indices: i32,
num_mask_indices: i32,
} }
enum DrawType { enum DrawType {

View File

@ -283,7 +283,8 @@ struct Draw {
draw_type: DrawType, draw_type: DrawType,
vertex_buffer: wgpu::Buffer, vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer, index_buffer: wgpu::Buffer,
index_count: u32, num_indices: u32,
num_mask_indices: u32,
} }
#[allow(dead_code)] #[allow(dead_code)]
@ -590,7 +591,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
draw_type: DrawType::Color, draw_type: DrawType::Color,
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
index_count, num_indices: index_count,
num_mask_indices: draw.mask_index_count,
}, },
TessDrawType::Gradient(gradient) => { TessDrawType::Gradient(gradient) => {
// TODO: Extract to function? // TODO: Extract to function?
@ -672,7 +674,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
}, },
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
index_count, num_indices: index_count,
num_mask_indices: draw.mask_index_count,
} }
} }
TessDrawType::Bitmap(bitmap) => { TessDrawType::Bitmap(bitmap) => {
@ -742,7 +745,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
}, },
vertex_buffer, vertex_buffer,
index_buffer, index_buffer,
index_count, num_indices: index_count,
num_mask_indices: draw.mask_index_count,
} }
} }
}); });
@ -1086,6 +1090,18 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
); );
for draw in &mesh.draws { for draw in &mesh.draws {
let num_indices = if self.mask_state != MaskState::DrawMaskStencil
&& self.mask_state != MaskState::ClearMaskStencil
{
draw.num_indices
} else {
// Omit strokes when drawing a mask stencil.
draw.num_mask_indices
};
if num_indices == 0 {
continue;
}
match &draw.draw_type { match &draw.draw_type {
DrawType::Color => { DrawType::Color => {
frame.render_pass.set_pipeline( frame.render_pass.set_pipeline(
@ -1146,7 +1162,7 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
} }
}; };
frame.render_pass.draw_indexed(0..draw.index_count, 0, 0..1); frame.render_pass.draw_indexed(0..num_indices, 0, 0..1);
} }
} }