vsvg_viewer/painters/
point_painter.rs

1use crate::engine::EngineRenderObjects;
2use crate::painters::Painter;
3use std::mem;
4use wgpu::util::DeviceExt;
5use wgpu::{
6    include_wgsl, vertex_attr_array, Buffer, ColorTargetState, PrimitiveTopology, RenderPass,
7    RenderPipeline,
8};
9
10#[repr(C)]
11#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
12pub(super) struct PointVertex {
13    pub(crate) position: [f32; 2],
14    pub(crate) color: u32,
15    pub(crate) size: f32,
16}
17
18pub(crate) struct PointPainterData {
19    instance_buffer: Buffer,
20    instance_count: u32,
21}
22
23impl PointPainterData {
24    pub fn new(
25        render_objects: &EngineRenderObjects,
26        vertices: impl IntoIterator<Item = [f32; 2]>,
27        color: u32,
28        size: f32,
29    ) -> Self {
30        vsvg::trace_function!();
31
32        let instances = vertices
33            .into_iter()
34            .map(|v| PointVertex {
35                position: v,
36                color,
37                size,
38            })
39            .collect::<Vec<_>>();
40
41        let instance_buffer =
42            render_objects
43                .device
44                .create_buffer_init(&wgpu::util::BufferInitDescriptor {
45                    label: Some("PointPainter instance buffer"),
46                    contents: bytemuck::cast_slice(instances.as_slice()),
47                    usage: wgpu::BufferUsages::VERTEX,
48                });
49
50        #[allow(clippy::cast_possible_truncation)]
51        Self {
52            instance_buffer,
53            instance_count: instances.len() as u32,
54        }
55    }
56}
57
58pub(crate) struct PointPainter {
59    render_pipeline: RenderPipeline,
60}
61
62impl PointPainter {
63    pub(crate) fn new(render_objects: &EngineRenderObjects) -> Self {
64        let vertex_buffer_layout = wgpu::VertexBufferLayout {
65            array_stride: mem::size_of::<PointVertex>() as wgpu::BufferAddress,
66            step_mode: wgpu::VertexStepMode::Instance,
67            attributes: &vertex_attr_array![
68                0 => Float32x2,
69                1 => Uint32,
70                2 => Float32,
71            ],
72        };
73
74        let shader = render_objects
75            .device
76            .create_shader_module(include_wgsl!("../shaders/point.wgsl"));
77
78        let pipeline_layout =
79            render_objects
80                .device
81                .create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
82                    label: None,
83                    bind_group_layouts: &[&render_objects.camera_bind_group_layout],
84                    push_constant_ranges: &[],
85                });
86
87        // enable alpha blending
88        let target = ColorTargetState {
89            format: render_objects.target_format,
90            blend: Some(wgpu::BlendState {
91                color: wgpu::BlendComponent {
92                    src_factor: wgpu::BlendFactor::SrcAlpha,
93                    dst_factor: wgpu::BlendFactor::OneMinusSrcAlpha,
94                    operation: wgpu::BlendOperation::Add,
95                },
96                alpha: wgpu::BlendComponent {
97                    src_factor: wgpu::BlendFactor::OneMinusDstAlpha,
98                    dst_factor: wgpu::BlendFactor::DstAlpha,
99                    operation: wgpu::BlendOperation::Add,
100                },
101            }),
102            write_mask: wgpu::ColorWrites::ALL,
103        };
104
105        let render_pipeline =
106            render_objects
107                .device
108                .create_render_pipeline(&wgpu::RenderPipelineDescriptor {
109                    label: Some("PointPainter pipeline"),
110                    layout: Some(&pipeline_layout),
111                    vertex: wgpu::VertexState {
112                        module: &shader,
113                        entry_point: "vs_main",
114                        buffers: &[vertex_buffer_layout],
115                    },
116                    fragment: Some(wgpu::FragmentState {
117                        module: &shader,
118                        entry_point: "fs_main",
119                        targets: &[Some(target)],
120                    }),
121                    primitive: wgpu::PrimitiveState {
122                        topology: PrimitiveTopology::TriangleStrip,
123                        ..Default::default()
124                    },
125                    depth_stencil: None,
126                    multisample: wgpu::MultisampleState::default(),
127                    multiview: None,
128                });
129
130        Self { render_pipeline }
131    }
132}
133
134impl Painter for PointPainter {
135    type Data = PointPainterData;
136
137    fn draw<'a>(
138        &'a self,
139        rpass: &mut RenderPass<'a>,
140        camera_bind_group: &'a wgpu::BindGroup,
141        data: &'a Self::Data,
142    ) {
143        rpass.set_pipeline(&self.render_pipeline);
144        rpass.set_bind_group(0, camera_bind_group, &[]);
145        rpass.set_vertex_buffer(0, data.instance_buffer.slice(..));
146        rpass.draw(0..4, 0..data.instance_count);
147    }
148}