rootvg_quad/pipeline/
solid.rs

1use rootvg_core::{
2    buffer::Buffer,
3    math::{PhysicalSizeI32, ScaleFactor},
4    pipeline::DefaultConstantUniforms,
5};
6use wgpu::PipelineCompilationOptions;
7
8use super::INITIAL_INSTANCES;
9
10use crate::SolidQuadPrimitive;
11
12pub struct SolidQuadBatchBuffer {
13    buffer: Buffer<SolidQuadPrimitive>,
14    num_primitives: usize,
15}
16
17#[derive(Debug)]
18pub struct SolidQuadPipeline {
19    pipeline: wgpu::RenderPipeline,
20
21    constants_buffer: wgpu::Buffer,
22    constants_bind_group: wgpu::BindGroup,
23
24    screen_size: PhysicalSizeI32,
25    scale_factor: ScaleFactor,
26}
27
28impl SolidQuadPipeline {
29    pub fn new(
30        device: &wgpu::Device,
31        format: wgpu::TextureFormat,
32        multisample: wgpu::MultisampleState,
33    ) -> Self {
34        let (constants_layout, constants_buffer, constants_bind_group) =
35            DefaultConstantUniforms::layout_buffer_and_bind_group(device);
36
37        let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
38            label: Some("rootvg-quad solid pipeline layout"),
39            push_constant_ranges: &[],
40            bind_group_layouts: &[&constants_layout],
41        });
42
43        let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
44            label: Some("rootvg-quad solid shader"),
45            source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(concat!(
46                include_str!("../shader/quad.wgsl"),
47                "\n",
48                include_str!("../shader/solid.wgsl"),
49            ))),
50        });
51
52        let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
53            label: Some("rootvg-quad solid pipeline"),
54            layout: Some(&layout),
55            vertex: wgpu::VertexState {
56                module: &shader,
57                entry_point: "solid_vs_main",
58                buffers: &[wgpu::VertexBufferLayout {
59                    array_stride: std::mem::size_of::<SolidQuadPrimitive>() as u64,
60                    step_mode: wgpu::VertexStepMode::Instance,
61                    attributes: &wgpu::vertex_attr_array!(
62                        // Color
63                        0 => Float32x4,
64                        // Position
65                        1 => Float32x2,
66                        // Size
67                        2 => Float32x2,
68                        // Border color
69                        3 => Float32x4,
70                        // Border radius
71                        4 => Float32x4,
72                        // Border width
73                        5 => Float32,
74                        /*
75                        // Shadow color
76                        6 => Float32x4,
77                        // Shadow offset
78                        7 => Float32x2,
79                        // Shadow blur radius
80                        8 => Float32,
81                        */
82                    ),
83                }],
84                compilation_options: PipelineCompilationOptions::default(),
85            },
86            fragment: Some(wgpu::FragmentState {
87                module: &shader,
88                entry_point: "solid_fs_main",
89                targets: &super::color_target_state(format),
90                compilation_options: PipelineCompilationOptions::default(),
91            }),
92            primitive: wgpu::PrimitiveState {
93                topology: wgpu::PrimitiveTopology::TriangleList,
94                front_face: wgpu::FrontFace::Cw,
95                ..Default::default()
96            },
97            depth_stencil: None,
98            multisample,
99            multiview: None,
100        });
101
102        Self {
103            constants_buffer,
104            constants_bind_group,
105            pipeline,
106            screen_size: PhysicalSizeI32::default(),
107            scale_factor: ScaleFactor::default(),
108        }
109    }
110
111    pub fn create_batch(&mut self, device: &wgpu::Device) -> SolidQuadBatchBuffer {
112        SolidQuadBatchBuffer {
113            buffer: Buffer::new(
114                device,
115                "rootvg-quad solid buffer",
116                INITIAL_INSTANCES,
117                wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
118            ),
119            num_primitives: 0,
120        }
121    }
122
123    pub fn start_preparations(
124        &mut self,
125        _device: &wgpu::Device,
126        queue: &wgpu::Queue,
127        screen_size: PhysicalSizeI32,
128        scale_factor: ScaleFactor,
129    ) {
130        if self.screen_size == screen_size && self.scale_factor == scale_factor {
131            return;
132        }
133
134        self.screen_size = screen_size;
135        self.scale_factor = scale_factor;
136
137        DefaultConstantUniforms::prepare_buffer(
138            &self.constants_buffer,
139            screen_size,
140            scale_factor,
141            queue,
142        );
143    }
144
145    pub fn prepare_batch(
146        &mut self,
147        batch: &mut SolidQuadBatchBuffer,
148        primitives: &[SolidQuadPrimitive],
149        device: &wgpu::Device,
150        queue: &wgpu::Queue,
151    ) {
152        let _ = batch
153            .buffer
154            .expand_to_fit_new_size(device, primitives.len());
155        let _ = batch.buffer.write(queue, 0, primitives);
156
157        batch.num_primitives = primitives.len();
158    }
159
160    pub fn render_batch<'pass>(
161        &'pass self,
162        batch: &'pass SolidQuadBatchBuffer,
163        render_pass: &mut wgpu::RenderPass<'pass>,
164    ) {
165        if batch.num_primitives == 0 {
166            return;
167        }
168
169        render_pass.set_pipeline(&self.pipeline);
170        render_pass.set_bind_group(0, &self.constants_bind_group, &[]);
171
172        render_pass.set_vertex_buffer(0, batch.buffer.slice(0..batch.num_primitives));
173
174        render_pass.draw(0..6, 0..batch.num_primitives as u32);
175    }
176}