avenger_wgpu/marks/
instanced_mark.rs

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        // Uniforms
71        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        // Create Texture
101        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        // Create sampler
114        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        // Create texture/sampler bind grous
125        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                        // This should match the filterable field of the
142                        // corresponding Texture entry above.
143                        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        // Shaders
166        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}