1use bytemuck::{Pod, Zeroable};
10use wgpu::util::DeviceExt;
11
12use crate::application::Application;
13use crate::texture::render_texture::RenderTexture;
14
15const MAX_PATH_POINTS: usize = 32;
16
17#[repr(C)]
18#[derive(Clone, Copy, Pod, Zeroable)]
19struct PathMaskTextureUniforms {
20 count: u32,
21 invert: u32,
22 _pad: [u32; 2],
23 points: [[f32; 4]; MAX_PATH_POINTS],
24}
25
26pub(crate) struct PathMaskTexturePipeline {
27 pipeline: wgpu::RenderPipeline,
28 bind_group_layout: wgpu::BindGroupLayout,
29}
30
31impl PathMaskTexturePipeline {
32 pub(crate) fn new(app: &Application, output_format: wgpu::TextureFormat) -> Self {
33 let device = app.device();
34
35 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
36 label: Some("wisp::path_mask_texture bg layout"),
37 entries: &[wgpu::BindGroupLayoutEntry {
38 binding: 0,
39 visibility: wgpu::ShaderStages::FRAGMENT,
40 ty: wgpu::BindingType::Buffer {
41 ty: wgpu::BufferBindingType::Uniform,
42 has_dynamic_offset: false,
43 min_binding_size: None,
44 },
45 count: None,
46 }],
47 });
48
49 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
50 label: Some("wisp::path_mask_texture pipeline layout"),
51 bind_group_layouts: &[&bind_group_layout],
52 push_constant_ranges: &[],
53 });
54
55 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
56 label: Some("wisp::path_mask_texture shader"),
57 source: wgpu::ShaderSource::Wgsl(
58 include_str!("../../shaders/path_mask_texture.wgsl").into(),
59 ),
60 });
61
62 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
63 label: Some("wisp::path_mask_texture pipeline"),
64 layout: Some(&pipeline_layout),
65 vertex: wgpu::VertexState {
66 module: &shader,
67 entry_point: Some("main_vs"),
68 buffers: &[],
69 compilation_options: wgpu::PipelineCompilationOptions::default(),
70 },
71 fragment: Some(wgpu::FragmentState {
72 module: &shader,
73 entry_point: Some("main_fs"),
74 targets: &[Some(wgpu::ColorTargetState {
75 format: output_format,
76 blend: Some(wgpu::BlendState::REPLACE),
77 write_mask: wgpu::ColorWrites::ALL,
78 })],
79 compilation_options: wgpu::PipelineCompilationOptions::default(),
80 }),
81 primitive: wgpu::PrimitiveState::default(),
82 depth_stencil: None,
83 multisample: wgpu::MultisampleState::default(),
84 multiview: None,
85 cache: None,
86 });
87
88 Self {
89 pipeline,
90 bind_group_layout,
91 }
92 }
93
94 pub(crate) fn generate(
95 &self,
96 app: &Application,
97 points: &[glam::Vec2],
98 w: u32,
99 h: u32,
100 output_format: wgpu::TextureFormat,
101 ) -> RenderTexture {
102 let rt = RenderTexture::with_format(app, w, h, output_format);
103 self.render_into(app, points, false, &rt);
104 rt
105 }
106
107 pub(crate) fn render_into(
108 &self,
109 app: &Application,
110 points: &[glam::Vec2],
111 invert: bool,
112 output: &RenderTexture,
113 ) {
114 let mut padded = [[0.0_f32; 4]; MAX_PATH_POINTS];
115 let count = points.len().min(MAX_PATH_POINTS);
116 for (slot, p) in padded.iter_mut().zip(points.iter()).take(count) {
117 *slot = [p.x, p.y, 0.0, 0.0];
118 }
119 let count_u32 = u32::try_from(count).unwrap_or(0);
120
121 let uniforms = PathMaskTextureUniforms {
122 count: count_u32,
123 invert: u32::from(invert),
124 _pad: [0, 0],
125 points: padded,
126 };
127 let buffer = app
128 .device()
129 .create_buffer_init(&wgpu::util::BufferInitDescriptor {
130 label: Some("wisp::path_mask_texture uniforms"),
131 contents: bytemuck::bytes_of(&uniforms),
132 usage: wgpu::BufferUsages::UNIFORM,
133 });
134
135 let bg = app.device().create_bind_group(&wgpu::BindGroupDescriptor {
136 label: Some("wisp::path_mask_texture bg"),
137 layout: &self.bind_group_layout,
138 entries: &[wgpu::BindGroupEntry {
139 binding: 0,
140 resource: buffer.as_entire_binding(),
141 }],
142 });
143
144 let mut encoder = app
145 .device()
146 .create_command_encoder(&wgpu::CommandEncoderDescriptor {
147 label: Some("wisp::path_mask_texture encoder"),
148 });
149 {
150 let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
151 label: Some("wisp::path_mask_texture pass"),
152 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
153 view: output.view(),
154 resolve_target: None,
155 ops: wgpu::Operations {
156 load: wgpu::LoadOp::Clear(wgpu::Color::TRANSPARENT),
157 store: wgpu::StoreOp::Store,
158 },
159 })],
160 depth_stencil_attachment: None,
161 timestamp_writes: None,
162 occlusion_query_set: None,
163 });
164 pass.set_pipeline(&self.pipeline);
165 pass.set_bind_group(0, &bg, &[]);
166 pass.draw(0..3, 0..1);
167 }
168 app.queue().submit(std::iter::once(encoder.finish()));
169 }
170}