1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! The mask-apply pass: modulate a window-sized projection's alpha by a mask
//! sampled in the mask's own plane space (`apply_mask.wgsl`).
//!
//! This is the de-fused replacement for the fused mask sampling that used to
//! live inside the layer-blend pass. A masked host composites its content into
//! an isolated projection; this pass multiplies that projection's alpha by the
//! mask before it blends down onto the parent — so the mask never samples the
//! host layer's texture or geometry. See `gpu::compositor` projection path.
/// Per-pass uniform: the mask texture's plane geometry + the isolated-debug
/// flag. Matches `MaskUniform` in `apply_mask.wgsl`. A zero `mask_size` is the
/// "no footprint" sentinel (reveal everywhere).
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
pub struct MaskUniform {
pub mask_offset: [f32; 2],
pub mask_size: [f32; 2],
pub isolated: u32,
pub _pad0: u32,
pub _pad1: u32,
pub _pad2: u32,
}
pub struct ApplyMaskPipeline {
pipeline: wgpu::RenderPipeline,
/// Group 0: projection texture + sampler + mask uniform.
pub bind_group_layout: wgpu::BindGroupLayout,
}
impl ApplyMaskPipeline {
/// Build the pipeline. Group 1 (mask texture) reuses the blend pipelines'
/// `mask_bind_group_layout`; group 2 (canvas geometry) reuses their
/// `canvas_bind_group_layout` — one shared layout per resource kind.
pub fn new(
device: &wgpu::Device,
format: wgpu::TextureFormat,
mask_bind_group_layout: &wgpu::BindGroupLayout,
canvas_bind_group_layout: &wgpu::BindGroupLayout,
) -> Self {
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("apply-mask-bgl"),
entries: &[
// binding 0: projection texture
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
// binding 1: sampler
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
// binding 2: mask uniform (offset/size/isolated)
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("apply-mask-pipeline-layout"),
bind_group_layouts: &[
Some(&bind_group_layout),
Some(mask_bind_group_layout),
Some(canvas_bind_group_layout),
],
immediate_size: 0,
});
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("apply-mask-shader"),
source: wgpu::ShaderSource::Wgsl(
crate::gpu::canvas_lib::with_canvas_lib(include_str!(
"../../shaders/apply_mask.wgsl"
))
.into(),
),
});
let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("apply-mask-pipeline"),
layout: Some(&pipeline_layout),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[],
compilation_options: Default::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format,
blend: None,
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: Default::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview_mask: None,
cache: None,
});
ApplyMaskPipeline {
pipeline,
bind_group_layout,
}
}
pub fn pipeline(&self) -> &wgpu::RenderPipeline {
&self.pipeline
}
}