render: Fix masking issues on wgpu/webgl backends
Change the usage of the stencil buffer to avoid running out of stencil bits when too many nested masks are active. This also cleans things up on wgpu which requires us to make pipeline states in advice; now we only need a few stencil states for masking as opposed to hundreds.
This commit is contained in:
parent
8ba5dbfa49
commit
4558be948e
|
@ -81,6 +81,12 @@ dependencies = [
|
|||
"num-traits 0.2.12",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "array-macro"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "06e97b4e522f9e55523001238ac59d13a8603af57f69980de5d8de4bbbe8ada6"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
|
@ -909,6 +915,27 @@ version = "0.3.6"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
|
||||
|
||||
[[package]]
|
||||
name = "enum-map"
|
||||
version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f81f09b9cb18af2ea1da2688a1d6b1762b4f938d7495bb034bce48d4c608043"
|
||||
dependencies = [
|
||||
"array-macro",
|
||||
"enum-map-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enum-map-derive"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e57001dfb2532f5a103ff869656887fae9a8defa7d236f3e39d2ee86ed629ad7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "enumset"
|
||||
version = "1.0.1"
|
||||
|
@ -2595,6 +2622,7 @@ version = "0.1.0"
|
|||
dependencies = [
|
||||
"bytemuck",
|
||||
"clap",
|
||||
"enum-map",
|
||||
"futures",
|
||||
"image",
|
||||
"jpeg-decoder",
|
||||
|
|
|
@ -40,6 +40,7 @@ pub trait RenderBackend: Downcast {
|
|||
fn draw_letterbox(&mut self, letterbox: Letterbox);
|
||||
fn push_mask(&mut self);
|
||||
fn activate_mask(&mut self);
|
||||
fn deactivate_mask(&mut self);
|
||||
fn pop_mask(&mut self);
|
||||
}
|
||||
impl_downcast!(RenderBackend);
|
||||
|
@ -143,6 +144,7 @@ impl RenderBackend for NullRenderer {
|
|||
fn draw_letterbox(&mut self, _letterbox: Letterbox) {}
|
||||
fn push_mask(&mut self) {}
|
||||
fn activate_mask(&mut self) {}
|
||||
fn deactivate_mask(&mut self) {}
|
||||
fn pop_mask(&mut self) {}
|
||||
}
|
||||
|
||||
|
|
|
@ -1150,18 +1150,22 @@ pub fn render_children<'gc>(
|
|||
children: &std::collections::BTreeMap<Depth, DisplayObject<'gc>>,
|
||||
) {
|
||||
let mut clip_depth = 0;
|
||||
let mut clip_depth_stack = vec![];
|
||||
let mut clip_depth_stack: Vec<(Depth, DisplayObject<'_>)> = vec![];
|
||||
for (&depth, &child) in children {
|
||||
// Check if we need to pop off a mask.
|
||||
// This must be a while loop because multiple masks can be popped
|
||||
// at the same dpeth.
|
||||
while clip_depth > 0 && depth >= clip_depth {
|
||||
// Clear the mask stencil and pop the mask.
|
||||
let (prev_clip_depth, clip_child) = clip_depth_stack.pop().unwrap();
|
||||
clip_depth = prev_clip_depth;
|
||||
context.renderer.deactivate_mask();
|
||||
clip_child.render(context);
|
||||
context.renderer.pop_mask();
|
||||
clip_depth = clip_depth_stack.pop().unwrap();
|
||||
}
|
||||
if child.clip_depth() > 0 && child.allow_as_mask() {
|
||||
// Push and render the mask.
|
||||
clip_depth_stack.push(clip_depth);
|
||||
clip_depth_stack.push((clip_depth, child));
|
||||
clip_depth = child.clip_depth();
|
||||
context.renderer.push_mask();
|
||||
child.render(context);
|
||||
|
@ -1172,9 +1176,11 @@ pub fn render_children<'gc>(
|
|||
}
|
||||
}
|
||||
|
||||
while !clip_depth_stack.is_empty() {
|
||||
// Pop any remaining masks.
|
||||
for (_, clip_child) in clip_depth_stack.into_iter().rev() {
|
||||
context.renderer.deactivate_mask();
|
||||
clip_child.render(context);
|
||||
context.renderer.pop_mask();
|
||||
clip_depth_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1029,6 +1029,11 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
|
|||
self.render_layout_box(context, layout_box);
|
||||
}
|
||||
|
||||
context.renderer.deactivate_mask();
|
||||
context.renderer.draw_rect(
|
||||
Color::from_rgb(0, 0xff),
|
||||
&(context.transform_stack.transform().matrix * mask),
|
||||
);
|
||||
context.renderer.pop_mask();
|
||||
|
||||
context.transform_stack.pop();
|
||||
|
|
|
@ -31,6 +31,7 @@ pub struct WebCanvasRenderBackend {
|
|||
viewport_height: u32,
|
||||
use_color_transform_hack: bool,
|
||||
pixelated_property_value: &'static str,
|
||||
deactivating_mask: bool,
|
||||
}
|
||||
|
||||
/// Canvas-drawable shape data extracted from an SWF file.
|
||||
|
@ -213,6 +214,7 @@ impl WebCanvasRenderBackend {
|
|||
viewport_width: 0,
|
||||
viewport_height: 0,
|
||||
use_color_transform_hack: is_firefox,
|
||||
deactivating_mask: false,
|
||||
|
||||
// For rendering non-smoothed bitmaps.
|
||||
// crisp-edges works in Firefox, pixelated works in Chrome (and others)?
|
||||
|
@ -551,6 +553,8 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
self.context.set_fill_style(&color.into());
|
||||
self.context
|
||||
.fill_rect(0.0, 0.0, width.into(), height.into());
|
||||
|
||||
self.deactivating_mask = false;
|
||||
}
|
||||
|
||||
fn end_frame(&mut self) {
|
||||
|
@ -558,6 +562,10 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
}
|
||||
|
||||
fn render_bitmap(&mut self, bitmap: BitmapHandle, transform: &Transform) {
|
||||
if self.deactivating_mask {
|
||||
return;
|
||||
}
|
||||
|
||||
self.set_transform(&transform.matrix);
|
||||
self.set_color_filter(transform);
|
||||
if let Some(bitmap) = self.bitmaps.get(bitmap.0) {
|
||||
|
@ -569,6 +577,10 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
}
|
||||
|
||||
fn render_shape(&mut self, shape: ShapeHandle, transform: &Transform) {
|
||||
if self.deactivating_mask {
|
||||
return;
|
||||
}
|
||||
|
||||
self.set_transform(&transform.matrix);
|
||||
if let Some(shape) = self.shapes.get(shape.0) {
|
||||
for command in shape.0.iter() {
|
||||
|
@ -629,6 +641,10 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
}
|
||||
|
||||
fn draw_rect(&mut self, color: Color, matrix: &Matrix) {
|
||||
if self.deactivating_mask {
|
||||
return;
|
||||
}
|
||||
|
||||
self.set_transform(matrix);
|
||||
self.clear_color_filter();
|
||||
|
||||
|
@ -685,7 +701,12 @@ impl RenderBackend for WebCanvasRenderBackend {
|
|||
// We render the maskee clips to the second render target.
|
||||
self.push_render_target();
|
||||
}
|
||||
fn deactivate_mask(&mut self) {
|
||||
self.deactivating_mask = true;
|
||||
}
|
||||
fn pop_mask(&mut self) {
|
||||
self.deactivating_mask = false;
|
||||
|
||||
let (maskee_canvas, maskee_context) = self.pop_render_target();
|
||||
let (masker_canvas, _masker_context) = self.pop_render_target();
|
||||
|
||||
|
|
|
@ -23,6 +23,14 @@ const GRADIENT_FRAGMENT_GLSL: &str = include_str!("../shaders/gradient.frag");
|
|||
const BITMAP_FRAGMENT_GLSL: &str = include_str!("../shaders/bitmap.frag");
|
||||
const NUM_VERTEX_ATTRIBUTES: u32 = 2;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum MaskState {
|
||||
NoMask,
|
||||
DrawMaskStencil,
|
||||
DrawMaskedContent,
|
||||
ClearMaskStencil,
|
||||
}
|
||||
|
||||
pub struct WebGlRenderBackend {
|
||||
/// WebGL1 context
|
||||
gl: Gl,
|
||||
|
@ -48,15 +56,11 @@ pub struct WebGlRenderBackend {
|
|||
|
||||
quad_shape: ShapeHandle,
|
||||
|
||||
mask_state: MaskState,
|
||||
num_masks: u32,
|
||||
num_masks_active: u32,
|
||||
write_stencil_mask: u32,
|
||||
test_stencil_mask: u32,
|
||||
next_stencil_mask: u32,
|
||||
mask_stack: Vec<(u32, u32)>,
|
||||
mask_state_dirty: bool,
|
||||
|
||||
active_program: *const ShaderProgram,
|
||||
mask_state_dirty: bool,
|
||||
blend_func: (u32, u32),
|
||||
mult_color: Option<[f32; 4]>,
|
||||
add_color: Option<[f32; 4]>,
|
||||
|
@ -191,15 +195,12 @@ impl WebGlRenderBackend {
|
|||
viewport_width: 500.0,
|
||||
viewport_height: 500.0,
|
||||
view_matrix: [[0.0; 4]; 4],
|
||||
|
||||
mask_state: MaskState::NoMask,
|
||||
num_masks: 0,
|
||||
num_masks_active: 0,
|
||||
write_stencil_mask: 0,
|
||||
test_stencil_mask: 0,
|
||||
next_stencil_mask: 1,
|
||||
mask_stack: vec![],
|
||||
mask_state_dirty: true,
|
||||
|
||||
active_program: std::ptr::null(),
|
||||
mask_state_dirty: true,
|
||||
blend_func: (Gl::SRC_ALPHA, Gl::ONE_MINUS_SRC_ALPHA),
|
||||
mult_color: None,
|
||||
add_color: None,
|
||||
|
@ -636,29 +637,31 @@ impl WebGlRenderBackend {
|
|||
fn set_stencil_state(&mut self) {
|
||||
// Set stencil state for masking, if necessary.
|
||||
if self.mask_state_dirty {
|
||||
if self.num_masks > 0 {
|
||||
self.gl.enable(Gl::STENCIL_TEST);
|
||||
if self.num_masks_active < self.num_masks {
|
||||
self.gl.stencil_mask(self.write_stencil_mask);
|
||||
self.gl
|
||||
.stencil_func(Gl::ALWAYS, self.write_stencil_mask as i32, 0xff);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::REPLACE);
|
||||
self.gl.color_mask(false, false, false, false);
|
||||
} else {
|
||||
self.gl.stencil_mask(0xff);
|
||||
self.gl.stencil_func(
|
||||
Gl::EQUAL,
|
||||
self.test_stencil_mask as i32,
|
||||
self.test_stencil_mask,
|
||||
);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::KEEP);
|
||||
self.gl.color_mask(true, true, true, true);
|
||||
}
|
||||
} else {
|
||||
match self.mask_state {
|
||||
MaskState::NoMask => {
|
||||
self.gl.disable(Gl::STENCIL_TEST);
|
||||
self.gl.color_mask(true, true, true, true);
|
||||
}
|
||||
self.mask_state_dirty = false;
|
||||
MaskState::DrawMaskStencil => {
|
||||
self.gl.enable(Gl::STENCIL_TEST);
|
||||
self.gl
|
||||
.stencil_func(Gl::EQUAL, (self.num_masks - 1) as i32, 0xff);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::INCR);
|
||||
self.gl.color_mask(false, false, false, false);
|
||||
}
|
||||
MaskState::DrawMaskedContent => {
|
||||
self.gl.enable(Gl::STENCIL_TEST);
|
||||
self.gl.stencil_func(Gl::EQUAL, self.num_masks as i32, 0xff);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::KEEP);
|
||||
self.gl.color_mask(true, true, true, true);
|
||||
}
|
||||
MaskState::ClearMaskStencil => {
|
||||
self.gl.enable(Gl::STENCIL_TEST);
|
||||
self.gl.stencil_func(Gl::EQUAL, self.num_masks as i32, 0xff);
|
||||
self.gl.stencil_op(Gl::KEEP, Gl::KEEP, Gl::DECR);
|
||||
self.gl.color_mask(false, false, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -796,13 +799,9 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
}
|
||||
|
||||
fn begin_frame(&mut self, clear: Color) {
|
||||
self.num_masks = 0;
|
||||
self.num_masks_active = 0;
|
||||
self.write_stencil_mask = 0;
|
||||
self.test_stencil_mask = 0;
|
||||
self.next_stencil_mask = 1;
|
||||
|
||||
self.active_program = std::ptr::null();
|
||||
self.mask_state = MaskState::NoMask;
|
||||
self.num_masks = 0;
|
||||
self.mask_state_dirty = true;
|
||||
|
||||
self.mult_color = None;
|
||||
|
@ -1212,48 +1211,35 @@ impl RenderBackend for WebGlRenderBackend {
|
|||
}
|
||||
|
||||
fn push_mask(&mut self) {
|
||||
// Desktop draws the masker to the stencil buffer, one bit per mask.
|
||||
// Masks-within-masks are handled as a bitmask.
|
||||
// This does unfortunately mean we are limited in the number of masks at once (usually 8 bits).
|
||||
if self.next_stencil_mask >= 0x100 {
|
||||
// If we've reached the limit of masks, clear the stencil buffer and start over.
|
||||
// But this may not be correct if there is still a mask active (mask-within-mask).
|
||||
if self.test_stencil_mask != 0 {
|
||||
log::warn!(
|
||||
"Too many masks active for stencil buffer; possibly incorrect rendering"
|
||||
assert!(
|
||||
self.mask_state == MaskState::NoMask || self.mask_state == MaskState::DrawMaskedContent
|
||||
);
|
||||
}
|
||||
self.next_stencil_mask = 1;
|
||||
self.gl.stencil_mask(0xff);
|
||||
self.gl.clear_stencil(self.test_stencil_mask as i32);
|
||||
self.gl.clear(Gl::STENCIL_BUFFER_BIT);
|
||||
self.gl.clear_stencil(0);
|
||||
}
|
||||
self.num_masks += 1;
|
||||
self.mask_stack
|
||||
.push((self.write_stencil_mask, self.test_stencil_mask));
|
||||
self.write_stencil_mask = self.next_stencil_mask;
|
||||
self.test_stencil_mask |= self.next_stencil_mask;
|
||||
self.next_stencil_mask <<= 1;
|
||||
self.mask_state = MaskState::DrawMaskStencil;
|
||||
self.mask_state_dirty = true;
|
||||
}
|
||||
|
||||
fn activate_mask(&mut self) {
|
||||
self.num_masks_active += 1;
|
||||
assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskStencil);
|
||||
self.mask_state = MaskState::DrawMaskedContent;
|
||||
self.mask_state_dirty = true;
|
||||
}
|
||||
|
||||
fn deactivate_mask(&mut self) {
|
||||
assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskedContent);
|
||||
self.mask_state = MaskState::ClearMaskStencil;
|
||||
self.mask_state_dirty = true;
|
||||
}
|
||||
|
||||
fn pop_mask(&mut self) {
|
||||
if !self.mask_stack.is_empty() {
|
||||
assert!(self.num_masks > 0 && self.mask_state == MaskState::ClearMaskStencil);
|
||||
self.num_masks -= 1;
|
||||
self.num_masks_active -= 1;
|
||||
let (write, test) = self.mask_stack.pop().unwrap();
|
||||
self.write_stencil_mask = write;
|
||||
self.test_stencil_mask = test;
|
||||
self.mask_state_dirty = true;
|
||||
self.mask_state = if self.num_masks == 0 {
|
||||
MaskState::NoMask
|
||||
} else {
|
||||
log::warn!("Mask stack underflow\n");
|
||||
}
|
||||
MaskState::DrawMaskedContent
|
||||
};
|
||||
self.mask_state_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ futures = "0.3.6"
|
|||
bytemuck = "1.4.1"
|
||||
raw-window-handle = "0.3.3"
|
||||
clap = { version = "3.0.0-beta.2", optional = true }
|
||||
enum-map = "0.6.3"
|
||||
|
||||
[features]
|
||||
render_debug_labels = []
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::utils::{
|
|||
gradient_spread_mode_index, ruffle_path_to_lyon_path, swf_bitmap_to_gl_matrix,
|
||||
swf_to_gl_matrix,
|
||||
};
|
||||
use enum_map::Enum;
|
||||
use ruffle_core::color_transform::ColorTransform;
|
||||
use std::mem::replace;
|
||||
use std::rc::Rc;
|
||||
|
@ -59,17 +60,21 @@ pub struct WgpuRenderBackend<T: RenderTarget> {
|
|||
viewport_height: f32,
|
||||
view_matrix: [[f32; 4]; 4],
|
||||
textures: Vec<(swf::CharacterId, Texture)>,
|
||||
mask_state: MaskState,
|
||||
num_masks: u32,
|
||||
num_masks_active: u32,
|
||||
write_stencil_mask: u32,
|
||||
test_stencil_mask: u32,
|
||||
next_stencil_mask: u32,
|
||||
mask_stack: Vec<(u32, u32)>,
|
||||
quad_vbo: wgpu::Buffer,
|
||||
quad_ibo: wgpu::Buffer,
|
||||
quad_tex_transforms: wgpu::Buffer,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Enum)]
|
||||
pub enum MaskState {
|
||||
NoMask,
|
||||
DrawMaskStencil,
|
||||
DrawMaskedContent,
|
||||
ClearMaskStencil,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
struct Transforms {
|
||||
|
@ -238,12 +243,10 @@ impl<T: RenderTarget> WgpuRenderBackend<T> {
|
|||
viewport_height,
|
||||
view_matrix,
|
||||
textures: Vec::new(),
|
||||
|
||||
num_masks: 0,
|
||||
num_masks_active: 0,
|
||||
write_stencil_mask: 0,
|
||||
test_stencil_mask: 0,
|
||||
next_stencil_mask: 1,
|
||||
mask_stack: Vec::new(),
|
||||
mask_state: MaskState::NoMask,
|
||||
|
||||
quad_vbo,
|
||||
quad_ibo,
|
||||
quad_tex_transforms,
|
||||
|
@ -829,11 +832,9 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
None
|
||||
}
|
||||
};
|
||||
|
||||
self.mask_state = MaskState::NoMask;
|
||||
self.num_masks = 0;
|
||||
self.num_masks_active = 0;
|
||||
self.write_stencil_mask = 0;
|
||||
self.test_stencil_mask = 0;
|
||||
self.next_stencil_mask = 1;
|
||||
|
||||
if let Some((frame_output, encoder)) = &mut self.current_frame {
|
||||
let (color_attachment, resolve_target) = if self.msaa_sample_count >= 2 {
|
||||
|
@ -1006,21 +1007,22 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
}),
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.pipelines.bitmap.pipeline_for(
|
||||
self.num_masks,
|
||||
self.num_masks_active,
|
||||
self.test_stencil_mask,
|
||||
self.write_stencil_mask,
|
||||
));
|
||||
render_pass.set_pipeline(&self.pipelines.bitmap.pipeline_for(self.mask_state));
|
||||
render_pass.set_bind_group(0, &bind_group, &[]);
|
||||
render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..));
|
||||
render_pass.set_index_buffer(self.quad_ibo.slice(..));
|
||||
|
||||
if self.num_masks_active < self.num_masks {
|
||||
render_pass.set_stencil_reference(self.write_stencil_mask);
|
||||
} else {
|
||||
render_pass.set_stencil_reference(self.test_stencil_mask);
|
||||
match self.mask_state {
|
||||
MaskState::NoMask => (),
|
||||
MaskState::DrawMaskStencil => {
|
||||
debug_assert!(self.num_masks > 0);
|
||||
render_pass.set_stencil_reference(self.num_masks - 1);
|
||||
}
|
||||
MaskState::DrawMaskedContent | MaskState::ClearMaskStencil => {
|
||||
debug_assert!(self.num_masks > 0);
|
||||
render_pass.set_stencil_reference(self.num_masks);
|
||||
}
|
||||
};
|
||||
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
}
|
||||
|
@ -1115,28 +1117,14 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
for draw in &mesh.draws {
|
||||
match &draw.draw_type {
|
||||
DrawType::Color => {
|
||||
render_pass.set_pipeline(&self.pipelines.color.pipeline_for(
|
||||
self.num_masks,
|
||||
self.num_masks_active,
|
||||
self.test_stencil_mask,
|
||||
self.write_stencil_mask,
|
||||
));
|
||||
render_pass.set_pipeline(&self.pipelines.color.pipeline_for(self.mask_state));
|
||||
}
|
||||
DrawType::Gradient { .. } => {
|
||||
render_pass.set_pipeline(&self.pipelines.gradient.pipeline_for(
|
||||
self.num_masks,
|
||||
self.num_masks_active,
|
||||
self.test_stencil_mask,
|
||||
self.write_stencil_mask,
|
||||
));
|
||||
render_pass
|
||||
.set_pipeline(&self.pipelines.gradient.pipeline_for(self.mask_state));
|
||||
}
|
||||
DrawType::Bitmap { .. } => {
|
||||
render_pass.set_pipeline(&self.pipelines.bitmap.pipeline_for(
|
||||
self.num_masks,
|
||||
self.num_masks_active,
|
||||
self.test_stencil_mask,
|
||||
self.write_stencil_mask,
|
||||
));
|
||||
render_pass.set_pipeline(&self.pipelines.bitmap.pipeline_for(self.mask_state));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1144,11 +1132,17 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
render_pass.set_vertex_buffer(0, draw.vertex_buffer.slice(..));
|
||||
render_pass.set_index_buffer(draw.index_buffer.slice(..));
|
||||
|
||||
if self.num_masks_active < self.num_masks {
|
||||
render_pass.set_stencil_reference(self.write_stencil_mask);
|
||||
} else {
|
||||
render_pass.set_stencil_reference(self.test_stencil_mask);
|
||||
match self.mask_state {
|
||||
MaskState::NoMask => (),
|
||||
MaskState::DrawMaskStencil => {
|
||||
debug_assert!(self.num_masks > 0);
|
||||
render_pass.set_stencil_reference(self.num_masks - 1);
|
||||
}
|
||||
MaskState::DrawMaskedContent | MaskState::ClearMaskStencil => {
|
||||
debug_assert!(self.num_masks > 0);
|
||||
render_pass.set_stencil_reference(self.num_masks);
|
||||
}
|
||||
};
|
||||
|
||||
render_pass.draw_indexed(0..draw.index_count, 0, 0..1);
|
||||
}
|
||||
|
@ -1254,21 +1248,22 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
}),
|
||||
});
|
||||
|
||||
render_pass.set_pipeline(&self.pipelines.color.pipeline_for(
|
||||
self.num_masks,
|
||||
self.num_masks_active,
|
||||
self.test_stencil_mask,
|
||||
self.write_stencil_mask,
|
||||
));
|
||||
render_pass.set_pipeline(&self.pipelines.color.pipeline_for(self.mask_state));
|
||||
render_pass.set_bind_group(0, &bind_group, &[]);
|
||||
render_pass.set_vertex_buffer(0, self.quad_vbo.slice(..));
|
||||
render_pass.set_index_buffer(self.quad_ibo.slice(..));
|
||||
|
||||
if self.num_masks_active < self.num_masks {
|
||||
render_pass.set_stencil_reference(self.write_stencil_mask);
|
||||
} else {
|
||||
render_pass.set_stencil_reference(self.test_stencil_mask);
|
||||
match self.mask_state {
|
||||
MaskState::NoMask => (),
|
||||
MaskState::DrawMaskStencil => {
|
||||
debug_assert!(self.num_masks > 0);
|
||||
render_pass.set_stencil_reference(self.num_masks - 1);
|
||||
}
|
||||
MaskState::DrawMaskedContent | MaskState::ClearMaskStencil => {
|
||||
debug_assert!(self.num_masks > 0);
|
||||
render_pass.set_stencil_reference(self.num_masks);
|
||||
}
|
||||
};
|
||||
|
||||
render_pass.draw_indexed(0..6, 0, 0..1);
|
||||
}
|
||||
|
@ -1362,69 +1357,31 @@ impl<T: RenderTarget + 'static> RenderBackend for WgpuRenderBackend<T> {
|
|||
}
|
||||
|
||||
fn push_mask(&mut self) {
|
||||
// Desktop draws the masker to the stencil buffer, one bit per mask.
|
||||
// Masks-within-masks are handled as a bitmask.
|
||||
// This does unfortunately mean we are limited in the number of masks at once (8 bits).
|
||||
if self.next_stencil_mask >= 0x100 {
|
||||
// If we've reached the limit of masks, clear the stencil buffer and start over.
|
||||
// But this may not be correct if there is still a mask active (mask-within-mask).
|
||||
if self.test_stencil_mask != 0 {
|
||||
log::warn!(
|
||||
"Too many masks active for stencil buffer; possibly incorrect rendering"
|
||||
assert!(
|
||||
self.mask_state == MaskState::NoMask || self.mask_state == MaskState::DrawMaskedContent
|
||||
);
|
||||
}
|
||||
self.next_stencil_mask = 1;
|
||||
if let Some((frame_output, encoder)) = &mut self.current_frame {
|
||||
let (color_attachment, resolve_target) = if self.msaa_sample_count >= 2 {
|
||||
(&self.frame_buffer_view, Some(frame_output.view()))
|
||||
} else {
|
||||
(frame_output.view(), None)
|
||||
};
|
||||
encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
|
||||
color_attachments: &[wgpu::RenderPassColorAttachmentDescriptor {
|
||||
attachment: color_attachment,
|
||||
resolve_target,
|
||||
ops: wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: Some(
|
||||
wgpu::RenderPassDepthStencilAttachmentDescriptor {
|
||||
attachment: &self.depth_texture_view,
|
||||
depth_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Load,
|
||||
store: true,
|
||||
}),
|
||||
stencil_ops: Some(wgpu::Operations {
|
||||
load: wgpu::LoadOp::Clear(self.test_stencil_mask),
|
||||
store: true,
|
||||
}),
|
||||
},
|
||||
),
|
||||
});
|
||||
}
|
||||
}
|
||||
self.num_masks += 1;
|
||||
self.mask_stack
|
||||
.push((self.write_stencil_mask, self.test_stencil_mask));
|
||||
self.write_stencil_mask = self.next_stencil_mask;
|
||||
self.test_stencil_mask |= self.next_stencil_mask;
|
||||
self.next_stencil_mask <<= 1;
|
||||
self.mask_state = MaskState::DrawMaskStencil;
|
||||
}
|
||||
|
||||
fn activate_mask(&mut self) {
|
||||
self.num_masks_active += 1;
|
||||
assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskStencil);
|
||||
self.mask_state = MaskState::DrawMaskedContent;
|
||||
}
|
||||
|
||||
fn deactivate_mask(&mut self) {
|
||||
assert!(self.num_masks > 0 && self.mask_state == MaskState::DrawMaskedContent);
|
||||
self.mask_state = MaskState::ClearMaskStencil;
|
||||
}
|
||||
|
||||
fn pop_mask(&mut self) {
|
||||
if !self.mask_stack.is_empty() {
|
||||
assert!(self.num_masks > 0 && self.mask_state == MaskState::ClearMaskStencil);
|
||||
self.num_masks -= 1;
|
||||
self.num_masks_active -= 1;
|
||||
let (write, test) = self.mask_stack.pop().unwrap();
|
||||
self.write_stencil_mask = write;
|
||||
self.test_stencil_mask = test;
|
||||
}
|
||||
self.mask_state = if self.num_masks == 0 {
|
||||
MaskState::NoMask
|
||||
} else {
|
||||
MaskState::DrawMaskedContent
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
use crate::{Error, GPUVertex};
|
||||
use crate::{Error, GPUVertex, MaskState};
|
||||
use enum_map::{enum_map, EnumMap};
|
||||
use wgpu::vertex_attr_array;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ShapePipeline {
|
||||
pub write_mask_pipelines: Vec<wgpu::RenderPipeline>,
|
||||
pub read_mask_pipelines: Vec<wgpu::RenderPipeline>,
|
||||
pub mask_pipelines: EnumMap<MaskState, wgpu::RenderPipeline>,
|
||||
pub bind_layout: wgpu::BindGroupLayout,
|
||||
}
|
||||
|
||||
|
@ -16,18 +16,8 @@ pub struct Pipelines {
|
|||
}
|
||||
|
||||
impl ShapePipeline {
|
||||
pub fn pipeline_for(
|
||||
&self,
|
||||
num_masks: u32,
|
||||
num_masks_active: u32,
|
||||
read_mask: u32,
|
||||
write_mask: u32,
|
||||
) -> &wgpu::RenderPipeline {
|
||||
if num_masks_active < num_masks {
|
||||
&self.write_mask_pipelines[write_mask.trailing_zeros() as usize]
|
||||
} else {
|
||||
&self.read_mask_pipelines[read_mask as usize]
|
||||
}
|
||||
pub fn pipeline_for(&self, mask_state: MaskState) -> &wgpu::RenderPipeline {
|
||||
&self.mask_pipelines[mask_state]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,13 +152,11 @@ fn create_color_pipelines(
|
|||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let mut write_mask_pipelines = Vec::new();
|
||||
let mut read_mask_pipelines = Vec::new();
|
||||
|
||||
for i in 0..8 {
|
||||
let label = create_debug_label!("Color pipeline write mask {}", i);
|
||||
write_mask_pipelines.push(device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
label.as_deref(),
|
||||
let mask_pipelines = enum_map! {
|
||||
MaskState::NoMask => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::NoMask);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Color pipeline no mask").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
|
@ -176,22 +164,7 @@ fn create_color_pipelines(
|
|||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilStateDescriptor {
|
||||
front: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Replace,
|
||||
},
|
||||
back: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Replace,
|
||||
},
|
||||
read_mask: 0xff,
|
||||
write_mask: 1 << i,
|
||||
},
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
|
@ -205,17 +178,17 @@ fn create_color_pipelines(
|
|||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask: wgpu::ColorWrite::empty(),
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
)));
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
for i in 0..256 {
|
||||
let label = create_debug_label!("Color pipeline read mask {}", i);
|
||||
read_mask_pipelines.push(device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
label.as_deref(),
|
||||
MaskState::DrawMaskStencil => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::DrawMaskStencil);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Color pipeline draw mask stencil").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
|
@ -223,22 +196,7 @@ fn create_color_pipelines(
|
|||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilStateDescriptor {
|
||||
front: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
back: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
read_mask: i,
|
||||
write_mask: 0,
|
||||
},
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
|
@ -252,16 +210,80 @@ fn create_color_pipelines(
|
|||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
)));
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
MaskState::DrawMaskedContent => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::DrawMaskedContent);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Color pipeline draw masked content").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
))
|
||||
},
|
||||
|
||||
MaskState::ClearMaskStencil => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::ClearMaskStencil);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Color pipeline clear mask stencil").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
))
|
||||
},
|
||||
};
|
||||
|
||||
ShapePipeline {
|
||||
write_mask_pipelines,
|
||||
read_mask_pipelines,
|
||||
mask_pipelines,
|
||||
bind_layout,
|
||||
}
|
||||
}
|
||||
|
@ -330,13 +352,11 @@ fn create_bitmap_pipeline(
|
|||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let mut write_mask_pipelines = Vec::new();
|
||||
let mut read_mask_pipelines = Vec::new();
|
||||
|
||||
for i in 0..8 {
|
||||
let label = create_debug_label!("Bitmap pipeline write mask {}", i);
|
||||
write_mask_pipelines.push(device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
label.as_deref(),
|
||||
let mask_pipelines = enum_map! {
|
||||
MaskState::NoMask => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::NoMask);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Bitmap pipeline no mask").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
|
@ -344,22 +364,7 @@ fn create_bitmap_pipeline(
|
|||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilStateDescriptor {
|
||||
front: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Replace,
|
||||
},
|
||||
back: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Replace,
|
||||
},
|
||||
read_mask: 0xff,
|
||||
write_mask: 1 << i,
|
||||
},
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
|
@ -373,17 +378,17 @@ fn create_bitmap_pipeline(
|
|||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask: wgpu::ColorWrite::empty(),
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
)));
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
for i in 0..256 {
|
||||
let label = create_debug_label!("Bitmap pipeline read mask {}", i);
|
||||
read_mask_pipelines.push(device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
label.as_deref(),
|
||||
MaskState::DrawMaskStencil => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::DrawMaskStencil);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Bitmap pipeline draw mask stencil").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
|
@ -391,27 +396,12 @@ fn create_bitmap_pipeline(
|
|||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilStateDescriptor {
|
||||
front: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
back: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
read_mask: i,
|
||||
write_mask: 0,
|
||||
},
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::One,
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
|
@ -420,16 +410,80 @@ fn create_bitmap_pipeline(
|
|||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
)));
|
||||
))
|
||||
},
|
||||
|
||||
MaskState::DrawMaskedContent => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::DrawMaskedContent);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Bitmap pipeline draw masked content").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Equal,
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
))
|
||||
},
|
||||
|
||||
MaskState::ClearMaskStencil => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::ClearMaskStencil);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Bitmap pipeline clear mask stencil").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
ShapePipeline {
|
||||
write_mask_pipelines,
|
||||
read_mask_pipelines,
|
||||
mask_pipelines,
|
||||
bind_layout,
|
||||
}
|
||||
}
|
||||
|
@ -492,13 +546,11 @@ fn create_gradient_pipeline(
|
|||
push_constant_ranges: &[],
|
||||
});
|
||||
|
||||
let mut write_mask_pipelines = Vec::new();
|
||||
let mut read_mask_pipelines = Vec::new();
|
||||
|
||||
for i in 0..8 {
|
||||
let label = create_debug_label!("Gradient pipeline write mask {}", i);
|
||||
write_mask_pipelines.push(device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
label.as_deref(),
|
||||
let mask_pipelines = enum_map! {
|
||||
MaskState::NoMask => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::NoMask);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Gradient pipeline no mask").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
|
@ -506,22 +558,7 @@ fn create_gradient_pipeline(
|
|||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilStateDescriptor {
|
||||
front: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Replace,
|
||||
},
|
||||
back: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Replace,
|
||||
},
|
||||
read_mask: 0xff,
|
||||
write_mask: 1 << i,
|
||||
},
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
|
@ -535,17 +572,17 @@ fn create_gradient_pipeline(
|
|||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask: wgpu::ColorWrite::empty(),
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
)));
|
||||
}
|
||||
))
|
||||
},
|
||||
|
||||
for i in 0..256 {
|
||||
let label = create_debug_label!("Gradient pipeline read mask {}", i);
|
||||
read_mask_pipelines.push(device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
label.as_deref(),
|
||||
MaskState::DrawMaskStencil => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::DrawMaskStencil);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Gradient pipeline draw mask stencil").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
|
@ -553,22 +590,7 @@ fn create_gradient_pipeline(
|
|||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil: wgpu::StencilStateDescriptor {
|
||||
front: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
back: wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
read_mask: i,
|
||||
write_mask: 0,
|
||||
},
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
|
@ -582,16 +604,132 @@ fn create_gradient_pipeline(
|
|||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask: wgpu::ColorWrite::ALL,
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
)));
|
||||
))
|
||||
},
|
||||
|
||||
|
||||
MaskState::DrawMaskedContent => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::DrawMaskedContent);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Gradient pipeline draw masked content").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Equal,
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
))
|
||||
},
|
||||
|
||||
MaskState::ClearMaskStencil => {
|
||||
let (stencil, write_mask) = mask_render_state(MaskState::ClearMaskStencil);
|
||||
device.create_render_pipeline(&create_pipeline_descriptor(
|
||||
create_debug_label!("Gradient pipeline clear mask stencil").as_deref(),
|
||||
vertex_shader,
|
||||
fragment_shader,
|
||||
&pipeline_layout,
|
||||
Some(wgpu::DepthStencilStateDescriptor {
|
||||
format: wgpu::TextureFormat::Depth24PlusStencil8,
|
||||
depth_write_enabled: true,
|
||||
depth_compare: wgpu::CompareFunction::Always,
|
||||
stencil,
|
||||
}),
|
||||
&[wgpu::ColorStateDescriptor {
|
||||
format: wgpu::TextureFormat::Bgra8Unorm,
|
||||
color_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
alpha_blend: wgpu::BlendDescriptor {
|
||||
src_factor: wgpu::BlendFactor::SrcAlpha,
|
||||
dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
|
||||
operation: wgpu::BlendOperation::Add,
|
||||
},
|
||||
write_mask,
|
||||
}],
|
||||
vertex_buffers_description,
|
||||
msaa_sample_count,
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
ShapePipeline {
|
||||
write_mask_pipelines,
|
||||
read_mask_pipelines,
|
||||
mask_pipelines,
|
||||
bind_layout,
|
||||
}
|
||||
}
|
||||
|
||||
fn mask_render_state(state: MaskState) -> (wgpu::StencilStateDescriptor, wgpu::ColorWrite) {
|
||||
let (stencil_state, color_write) = match state {
|
||||
MaskState::NoMask => (
|
||||
wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Always,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
wgpu::ColorWrite::ALL,
|
||||
),
|
||||
MaskState::DrawMaskStencil => (
|
||||
wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::IncrementClamp,
|
||||
},
|
||||
wgpu::ColorWrite::empty(),
|
||||
),
|
||||
MaskState::DrawMaskedContent => (
|
||||
wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::Keep,
|
||||
},
|
||||
wgpu::ColorWrite::ALL,
|
||||
),
|
||||
MaskState::ClearMaskStencil => (
|
||||
wgpu::StencilStateFaceDescriptor {
|
||||
compare: wgpu::CompareFunction::Equal,
|
||||
fail_op: wgpu::StencilOperation::Keep,
|
||||
depth_fail_op: wgpu::StencilOperation::Keep,
|
||||
pass_op: wgpu::StencilOperation::DecrementClamp,
|
||||
},
|
||||
wgpu::ColorWrite::empty(),
|
||||
),
|
||||
};
|
||||
|
||||
(
|
||||
wgpu::StencilStateDescriptor {
|
||||
front: stencil_state.clone(),
|
||||
back: stencil_state,
|
||||
read_mask: 0xff,
|
||||
write_mask: 0xff,
|
||||
},
|
||||
color_write,
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue