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,
mesh: Vec<Draw>,
lyon_mesh: VertexBuffers<Vertex, u32>,
mask_index_count: Option<u32>,
is_stroke: bool,
}
impl ShapeTessellator {
@ -22,6 +24,8 @@ impl ShapeTessellator {
stroke_tess: StrokeTessellator::new(),
mesh: Vec::new(),
lyon_mesh: VertexBuffers::new(),
mask_index_count: None,
is_stroke: false,
}
}
@ -33,9 +37,9 @@ impl ShapeTessellator {
self.mesh = Vec::new();
self.lyon_mesh = VertexBuffers::new();
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 } => {
(*style, ruffle_path_to_lyon_path(commands, true))
(*style, ruffle_path_to_lyon_path(commands, true), false)
}
DrawPath::Stroke {
style,
@ -44,6 +48,7 @@ impl ShapeTessellator {
} => (
style.fill_style(),
ruffle_path_to_lyon_path(&commands, *is_closed),
true,
),
};
@ -107,10 +112,19 @@ impl ShapeTessellator {
}
};
if needs_flush {
// Non-solid color fills are isolated draw calls, so flush any pending color fill.
if needs_flush || (self.is_stroke && !next_is_stroke) {
// 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);
} 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 =
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());
self.mesh.push(Draw {
draw_type: draw,
mask_index_count: self
.mask_index_count
.unwrap_or(draw_mesh.indices.len() as u32),
vertices: draw_mesh.vertices,
indices: draw_mesh.indices,
});
self.mask_index_count = None;
}
}
@ -205,6 +223,7 @@ pub struct Draw {
pub draw_type: DrawType,
pub vertices: Vec<Vertex>,
pub indices: Vec<u32>,
pub mask_index_count: u32,
}
pub enum DrawType {

View File

@ -348,6 +348,7 @@ impl WebGlRenderBackend {
vertex_buffer,
index_buffer,
num_indices: 6,
num_mask_indices: 6,
}],
};
Ok(quad_mesh)
@ -501,6 +502,7 @@ impl WebGlRenderBackend {
let mut draws = Vec::with_capacity(lyon_mesh.len());
for draw in lyon_mesh {
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 vertex_buffer = self.gl.create_buffer().unwrap();
@ -565,6 +567,7 @@ impl WebGlRenderBackend {
vertex_buffer,
index_buffer,
num_indices,
num_mask_indices,
},
TessDrawType::Gradient(gradient) => Draw {
draw_type: DrawType::Gradient(Box::new(Gradient::from(gradient))),
@ -572,6 +575,7 @@ impl WebGlRenderBackend {
vertex_buffer,
index_buffer,
num_indices,
num_mask_indices,
},
TessDrawType::Bitmap(bitmap) => Draw {
draw_type: DrawType::Bitmap(BitmapDraw {
@ -584,6 +588,7 @@ impl WebGlRenderBackend {
vertex_buffer,
index_buffer,
num_indices,
num_mask_indices,
},
});
@ -947,6 +952,18 @@ impl RenderBackend for WebGlRenderBackend {
let mesh = &self.meshes[shape.0];
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));
let (program, src_blend, dst_blend) = match &draw.draw_type {
@ -1068,7 +1085,7 @@ impl RenderBackend for WebGlRenderBackend {
// Draw the triangles.
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,
vao: WebGlVertexArrayObject,
num_indices: i32,
num_mask_indices: i32,
}
enum DrawType {

View File

@ -283,7 +283,8 @@ struct Draw {
draw_type: DrawType,
vertex_buffer: wgpu::Buffer,
index_buffer: wgpu::Buffer,
index_count: u32,
num_indices: u32,
num_mask_indices: u32,
}
#[allow(dead_code)]
@ -590,7 +591,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
draw_type: DrawType::Color,
vertex_buffer,
index_buffer,
index_count,
num_indices: index_count,
num_mask_indices: draw.mask_index_count,
},
TessDrawType::Gradient(gradient) => {
// TODO: Extract to function?
@ -672,7 +674,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
},
vertex_buffer,
index_buffer,
index_count,
num_indices: index_count,
num_mask_indices: draw.mask_index_count,
}
}
TessDrawType::Bitmap(bitmap) => {
@ -742,7 +745,8 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
},
vertex_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 {
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 {
DrawType::Color => {
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);
}
}