1use crate::application::Application;
13use crate::texture::render_texture::RenderTexture;
14
15pub(crate) struct MaskComposePipeline {
16 pipeline: wgpu::RenderPipeline,
17 bind_group_layout: wgpu::BindGroupLayout,
18 sampler: wgpu::Sampler,
19}
20
21impl MaskComposePipeline {
22 pub(crate) fn new(app: &Application, output_format: wgpu::TextureFormat) -> Self {
23 let device = app.device();
24
25 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
26 label: Some("wisp::mask_compose bg layout"),
27 entries: &[
28 wgpu::BindGroupLayoutEntry {
29 binding: 0,
30 visibility: wgpu::ShaderStages::FRAGMENT,
31 ty: wgpu::BindingType::Texture {
32 sample_type: wgpu::TextureSampleType::Float { filterable: true },
33 view_dimension: wgpu::TextureViewDimension::D2,
34 multisampled: false,
35 },
36 count: None,
37 },
38 wgpu::BindGroupLayoutEntry {
39 binding: 1,
40 visibility: wgpu::ShaderStages::FRAGMENT,
41 ty: wgpu::BindingType::Texture {
42 sample_type: wgpu::TextureSampleType::Float { filterable: true },
43 view_dimension: wgpu::TextureViewDimension::D2,
44 multisampled: false,
45 },
46 count: None,
47 },
48 wgpu::BindGroupLayoutEntry {
49 binding: 2,
50 visibility: wgpu::ShaderStages::FRAGMENT,
51 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
52 count: None,
53 },
54 ],
55 });
56
57 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
58 label: Some("wisp::mask_compose pipeline layout"),
59 bind_group_layouts: &[&bind_group_layout],
60 push_constant_ranges: &[],
61 });
62
63 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
64 label: Some("wisp::mask_compose shader"),
65 source: wgpu::ShaderSource::Wgsl(
66 include_str!("../../shaders/mask_compose.wgsl").into(),
67 ),
68 });
69
70 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
71 label: Some("wisp::mask_compose pipeline"),
72 layout: Some(&pipeline_layout),
73 vertex: wgpu::VertexState {
74 module: &shader,
75 entry_point: Some("main_vs"),
76 buffers: &[],
77 compilation_options: wgpu::PipelineCompilationOptions::default(),
78 },
79 fragment: Some(wgpu::FragmentState {
80 module: &shader,
81 entry_point: Some("main_fs"),
82 targets: &[Some(wgpu::ColorTargetState {
83 format: output_format,
84 blend: Some(wgpu::BlendState::REPLACE),
85 write_mask: wgpu::ColorWrites::ALL,
86 })],
87 compilation_options: wgpu::PipelineCompilationOptions::default(),
88 }),
89 primitive: wgpu::PrimitiveState::default(),
90 depth_stencil: None,
91 multisample: wgpu::MultisampleState::default(),
92 multiview: None,
93 cache: None,
94 });
95
96 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
97 label: Some("wisp::mask_compose sampler"),
98 address_mode_u: wgpu::AddressMode::ClampToEdge,
99 address_mode_v: wgpu::AddressMode::ClampToEdge,
100 address_mode_w: wgpu::AddressMode::ClampToEdge,
101 mag_filter: wgpu::FilterMode::Linear,
102 min_filter: wgpu::FilterMode::Linear,
103 mipmap_filter: wgpu::FilterMode::Nearest,
104 ..Default::default()
105 });
106
107 Self {
108 pipeline,
109 bind_group_layout,
110 sampler,
111 }
112 }
113
114 pub(crate) fn apply(
115 &self,
116 app: &Application,
117 foreground: &RenderTexture,
118 mask: &RenderTexture,
119 output: &RenderTexture,
120 ) {
121 let bg = app.device().create_bind_group(&wgpu::BindGroupDescriptor {
122 label: Some("wisp::mask_compose bg"),
123 layout: &self.bind_group_layout,
124 entries: &[
125 wgpu::BindGroupEntry {
126 binding: 0,
127 resource: wgpu::BindingResource::TextureView(foreground.view()),
128 },
129 wgpu::BindGroupEntry {
130 binding: 1,
131 resource: wgpu::BindingResource::TextureView(mask.view()),
132 },
133 wgpu::BindGroupEntry {
134 binding: 2,
135 resource: wgpu::BindingResource::Sampler(&self.sampler),
136 },
137 ],
138 });
139
140 let mut encoder = app
141 .device()
142 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
143 label: Some("wisp::mask_compose encoder"),
144 });
145 {
146 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
147 label: Some("wisp::mask_compose pass"),
148 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
149 view: output.view(),
150 resolve_target: None,
151 ops: wgpu::Operations {
152 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
153 store: wgpu::StoreOp::Store,
154 },
155 })],
156 depth_stencil_attachment: None,
157 timestamp_writes: None,
158 occlusion_query_set: None,
159 });
160 pass.set_pipeline(&self.pipeline);
161 pass.set_bind_group(0, &bg, &[]);
162 pass.draw(0..3, 0..1);
163 }
164 app.queue().submit(std::iter::once(encoder.finish()));
165 }
166}