1use avenger::marks::group::Clip;
2use std::ops::Range;
3use wgpu::util::DeviceExt;
4use wgpu::{CommandBuffer, Device, Extent3d, ImageDataLayout, TextureFormat, TextureView};
5
6#[derive(Clone)]
7pub struct InstancedMarkBatch {
8 pub instances_range: Range<u32>,
9 pub image: Option<image::DynamicImage>,
10}
11
12pub trait InstancedMarkShader {
13 type Instance: bytemuck::Pod + bytemuck::Zeroable;
14 type Vertex: bytemuck::Pod + bytemuck::Zeroable;
15 type Uniform: bytemuck::Pod + bytemuck::Zeroable;
16
17 fn verts(&self) -> &[Self::Vertex];
18 fn indices(&self) -> &[u16];
19 fn instances(&self) -> &[Self::Instance];
20 fn uniform(&self) -> Self::Uniform;
21 fn batches(&self) -> &[InstancedMarkBatch];
22 fn texture_size(&self) -> Extent3d;
23
24 fn shader(&self) -> &str;
25 fn vertex_entry_point(&self) -> &str;
26 fn fragment_entry_point(&self) -> &str;
27 fn instance_desc(&self) -> wgpu::VertexBufferLayout<'static>;
28 fn vertex_desc(&self) -> wgpu::VertexBufferLayout<'static>;
29
30 fn mag_filter(&self) -> wgpu::FilterMode {
31 wgpu::FilterMode::Nearest
32 }
33 fn min_filter(&self) -> wgpu::FilterMode {
34 wgpu::FilterMode::Nearest
35 }
36 fn mipmap_filter(&self) -> wgpu::FilterMode {
37 wgpu::FilterMode::Nearest
38 }
39}
40
41pub struct InstancedMarkRenderer {
42 pub render_pipeline: wgpu::RenderPipeline,
43 pub vertex_buffer: wgpu::Buffer,
44 pub index_buffer: wgpu::Buffer,
45 pub num_indices: u32,
46 pub instance_buffer: wgpu::Buffer,
47 pub batches: Vec<InstancedMarkBatch>,
48 pub uniform_bind_group: wgpu::BindGroup,
49 pub texture: wgpu::Texture,
50 pub texture_size: wgpu::Extent3d,
51 pub texture_bind_group: wgpu::BindGroup,
52 pub clip: Clip,
53 pub scale: f32,
54}
55
56impl InstancedMarkRenderer {
57 pub fn new<I, V, U>(
58 device: &Device,
59 texture_format: TextureFormat,
60 sample_count: u32,
61 mark_shader: Box<dyn InstancedMarkShader<Instance = I, Vertex = V, Uniform = U>>,
62 clip: Clip,
63 scale: f32,
64 ) -> Self
65 where
66 I: bytemuck::Pod + bytemuck::Zeroable,
67 V: bytemuck::Pod + bytemuck::Zeroable,
68 U: bytemuck::Pod + bytemuck::Zeroable,
69 {
70 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
72 label: Some("Uniform Buffer"),
73 contents: bytemuck::cast_slice(&[mark_shader.uniform()]),
74 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
75 });
76
77 let uniform_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
78 entries: &[wgpu::BindGroupLayoutEntry {
79 binding: 0,
80 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
81 ty: wgpu::BindingType::Buffer {
82 ty: wgpu::BufferBindingType::Uniform,
83 has_dynamic_offset: false,
84 min_binding_size: None,
85 },
86 count: None,
87 }],
88 label: Some("chart_uniform_layout"),
89 });
90
91 let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
92 layout: &uniform_layout,
93 entries: &[wgpu::BindGroupEntry {
94 binding: 0,
95 resource: uniform_buffer.as_entire_binding(),
96 }],
97 label: Some("uniform_bind_group"),
98 });
99
100 let texture = device.create_texture(&wgpu::TextureDescriptor {
102 size: mark_shader.texture_size(),
103 mip_level_count: 1,
104 sample_count: 1,
105 dimension: wgpu::TextureDimension::D2,
106 format: wgpu::TextureFormat::Rgba8Unorm,
107 usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
108 label: Some("diffuse_texture"),
109 view_formats: &[],
110 });
111 let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
112
113 let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
115 address_mode_u: wgpu::AddressMode::ClampToEdge,
116 address_mode_v: wgpu::AddressMode::ClampToEdge,
117 address_mode_w: wgpu::AddressMode::ClampToEdge,
118 mag_filter: mark_shader.mag_filter(),
119 min_filter: mark_shader.min_filter(),
120 mipmap_filter: mark_shader.mipmap_filter(),
121 ..Default::default()
122 });
123
124 let texture_bind_group_layout =
126 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
127 entries: &[
128 wgpu::BindGroupLayoutEntry {
129 binding: 0,
130 visibility: wgpu::ShaderStages::FRAGMENT,
131 ty: wgpu::BindingType::Texture {
132 multisampled: false,
133 view_dimension: wgpu::TextureViewDimension::D2,
134 sample_type: wgpu::TextureSampleType::Float { filterable: true },
135 },
136 count: None,
137 },
138 wgpu::BindGroupLayoutEntry {
139 binding: 1,
140 visibility: wgpu::ShaderStages::FRAGMENT,
141 ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
144 count: None,
145 },
146 ],
147 label: Some("texture_bind_group_layout"),
148 });
149
150 let texture_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
151 layout: &texture_bind_group_layout,
152 entries: &[
153 wgpu::BindGroupEntry {
154 binding: 0,
155 resource: wgpu::BindingResource::TextureView(&texture_view),
156 },
157 wgpu::BindGroupEntry {
158 binding: 1,
159 resource: wgpu::BindingResource::Sampler(&sampler),
160 },
161 ],
162 label: Some("texture_bind_group"),
163 });
164
165 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
167 label: Some("Shader"),
168 source: wgpu::ShaderSource::Wgsl(mark_shader.shader().into()),
169 });
170
171 let render_pipeline_layout =
172 device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
173 label: Some("Render Pipeline Layout"),
174 bind_group_layouts: &[&uniform_layout, &texture_bind_group_layout],
175 push_constant_ranges: &[],
176 });
177
178 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
179 label: Some("Render Pipeline"),
180 layout: Some(&render_pipeline_layout),
181 vertex: wgpu::VertexState {
182 module: &shader,
183 entry_point: mark_shader.vertex_entry_point(),
184 compilation_options: Default::default(),
185 buffers: &[mark_shader.vertex_desc(), mark_shader.instance_desc()],
186 },
187 fragment: Some(wgpu::FragmentState {
188 module: &shader,
189 entry_point: mark_shader.fragment_entry_point(),
190 compilation_options: Default::default(),
191 targets: &[Some(wgpu::ColorTargetState {
192 format: texture_format,
193 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
194 write_mask: wgpu::ColorWrites::ALL,
195 })],
196 }),
197 primitive: wgpu::PrimitiveState {
198 topology: wgpu::PrimitiveTopology::TriangleList,
199 strip_index_format: None,
200 front_face: wgpu::FrontFace::Ccw,
201 cull_mode: Some(wgpu::Face::Back),
202 polygon_mode: wgpu::PolygonMode::Fill,
203 unclipped_depth: false,
204 conservative: false,
205 },
206 depth_stencil: None,
207 multisample: wgpu::MultisampleState {
208 count: sample_count,
209 mask: !0,
210 alpha_to_coverage_enabled: false,
211 },
212 multiview: None,
213 });
214
215 let vertex_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
216 label: Some("Vertex Buffer"),
217 contents: bytemuck::cast_slice(mark_shader.verts()),
218 usage: wgpu::BufferUsages::VERTEX,
219 });
220
221 let index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
222 label: Some("Index Buffer"),
223 contents: bytemuck::cast_slice(mark_shader.indices()),
224 usage: wgpu::BufferUsages::INDEX,
225 });
226 let num_indices = mark_shader.indices().len() as u32;
227
228 let instance_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
229 label: Some("Instance Buffer"),
230 contents: bytemuck::cast_slice(mark_shader.instances()),
231 usage: wgpu::BufferUsages::VERTEX,
232 });
233
234 Self {
235 render_pipeline,
236 vertex_buffer,
237 index_buffer,
238 batches: Vec::from(mark_shader.batches()),
239 num_indices,
240 instance_buffer,
241 uniform_bind_group,
242 texture,
243 texture_size: mark_shader.texture_size(),
244 texture_bind_group,
245 clip,
246 scale,
247 }
248 }
249
250 pub fn render(
251 &self,
252 device: &Device,
253 texture_view: &TextureView,
254 resolve_target: Option<&TextureView>,
255 ) -> CommandBuffer {
256 let mut mark_encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
257 label: Some("Mark Render Encoder"),
258 });
259
260 for batch in self.batches.iter() {
261 if let Some(img) = &batch.image {
262 let temp_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
263 label: Some("Temp Buffer"),
264 contents: img.to_rgba8().as_raw(),
265 usage: wgpu::BufferUsages::COPY_SRC,
266 });
267 mark_encoder.copy_buffer_to_texture(
268 wgpu::ImageCopyBuffer {
269 buffer: &temp_buffer,
270 layout: ImageDataLayout {
271 offset: 0,
272 bytes_per_row: Some(4 * self.texture_size.width),
273 rows_per_image: Some(self.texture_size.height),
274 },
275 },
276 wgpu::ImageCopyTexture {
277 texture: &self.texture,
278 mip_level: 0,
279 origin: wgpu::Origin3d::ZERO,
280 aspect: wgpu::TextureAspect::All,
281 },
282 self.texture_size,
283 );
284 }
285
286 {
287 let mut render_pass = mark_encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
288 label: Some("Mark Render Pass"),
289 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
290 view: texture_view,
291 resolve_target,
292 ops: wgpu::Operations {
293 load: wgpu::LoadOp::Load,
294 store: wgpu::StoreOp::Store,
295 },
296 })],
297 depth_stencil_attachment: None,
298 occlusion_query_set: None,
299 timestamp_writes: None,
300 });
301
302 render_pass.set_pipeline(&self.render_pipeline);
303 render_pass.set_bind_group(0, &self.uniform_bind_group, &[]);
304 render_pass.set_bind_group(1, &self.texture_bind_group, &[]);
305 render_pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
306 render_pass.set_vertex_buffer(1, self.instance_buffer.slice(..));
307 render_pass
308 .set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint16);
309
310 if let Clip::Rect {
311 x,
312 y,
313 width,
314 height,
315 } = self.clip
316 {
317 render_pass.set_scissor_rect(
318 (x * self.scale) as u32,
319 (y * self.scale) as u32,
320 (width * self.scale) as u32,
321 (height * self.scale) as u32,
322 );
323 }
324
325 render_pass.draw_indexed(0..self.num_indices, 0, batch.instances_range.clone());
326 }
327 }
328
329 mark_encoder.finish()
330 }
331}