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