tessera_ui_basic_components/pipelines/simple_rect/
pipeline.rs1use encase::{ShaderSize, ShaderType, StorageBuffer};
2use glam::{Vec2, Vec4};
3use tessera_ui::{
4 PxSize,
5 px::PxPosition,
6 renderer::drawer::pipeline::{DrawContext, DrawablePipeline},
7 wgpu::{self, include_wgsl, util::DeviceExt},
8};
9
10use super::command::SimpleRectCommand;
11
12#[repr(C)]
13#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
14struct Vertex {
15 position: [f32; 2],
16}
17
18#[derive(ShaderType, Clone, Copy, Debug, PartialEq)]
19struct RectUniform {
20 position: Vec4,
21 color: Vec4,
22 screen_size: Vec2,
23 #[shader(size(8))]
24 _padding: [f32; 2],
25}
26
27#[derive(ShaderType)]
28struct RectInstances {
29 #[shader(size(runtime))]
30 instances: Vec<RectUniform>,
31}
32
33pub struct SimpleRectPipeline {
34 pipeline: wgpu::RenderPipeline,
35 bind_group_layout: wgpu::BindGroupLayout,
36 quad_vertex_buffer: wgpu::Buffer,
37 quad_index_buffer: wgpu::Buffer,
38}
39
40impl SimpleRectPipeline {
41 pub fn new(
42 gpu: &wgpu::Device,
43 config: &wgpu::SurfaceConfiguration,
44 pipeline_cache: Option<&wgpu::PipelineCache>,
45 sample_count: u32,
46 ) -> Self {
47 let shader = gpu.create_shader_module(include_wgsl!("simple_rect.wgsl"));
48
49 let bind_group_layout = gpu.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
50 entries: &[wgpu::BindGroupLayoutEntry {
51 binding: 0,
52 visibility: wgpu::ShaderStages::VERTEX,
53 ty: wgpu::BindingType::Buffer {
54 ty: wgpu::BufferBindingType::Storage { read_only: true },
55 has_dynamic_offset: false,
56 min_binding_size: None,
57 },
58 count: None,
59 }],
60 label: Some("simple_rect_bind_group_layout"),
61 });
62
63 let pipeline_layout = gpu.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
64 label: Some("Simple Rect Pipeline Layout"),
65 bind_group_layouts: &[&bind_group_layout],
66 push_constant_ranges: &[],
67 });
68
69 let pipeline = gpu.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
70 label: Some("Simple Rect Pipeline"),
71 layout: Some(&pipeline_layout),
72 vertex: wgpu::VertexState {
73 module: &shader,
74 entry_point: Some("vs_main"),
75 buffers: &[wgpu::VertexBufferLayout {
76 array_stride: std::mem::size_of::<Vertex>() as wgpu::BufferAddress,
77 step_mode: wgpu::VertexStepMode::Vertex,
78 attributes: &wgpu::vertex_attr_array![0 => Float32x2],
79 }],
80 compilation_options: Default::default(),
81 },
82 primitive: wgpu::PrimitiveState {
83 topology: wgpu::PrimitiveTopology::TriangleList,
84 strip_index_format: None,
85 front_face: wgpu::FrontFace::Ccw,
86 cull_mode: Some(wgpu::Face::Back),
87 unclipped_depth: false,
88 polygon_mode: wgpu::PolygonMode::Fill,
89 conservative: false,
90 },
91 depth_stencil: None,
92 multisample: wgpu::MultisampleState {
93 count: sample_count,
94 mask: !0,
95 alpha_to_coverage_enabled: false,
96 },
97 fragment: Some(wgpu::FragmentState {
98 module: &shader,
99 entry_point: Some("fs_main"),
100 compilation_options: Default::default(),
101 targets: &[Some(wgpu::ColorTargetState {
102 format: config.format,
103 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
104 write_mask: wgpu::ColorWrites::ALL,
105 })],
106 }),
107 multiview: None,
108 cache: pipeline_cache,
109 });
110
111 let quad_vertices = [
112 Vertex {
113 position: [0.0, 0.0],
114 },
115 Vertex {
116 position: [1.0, 0.0],
117 },
118 Vertex {
119 position: [1.0, 1.0],
120 },
121 Vertex {
122 position: [0.0, 1.0],
123 },
124 ];
125 let quad_vertex_buffer = gpu.create_buffer_init(&wgpu::util::BufferInitDescriptor {
126 label: Some("Simple Rect Quad Vertex Buffer"),
127 contents: bytemuck::cast_slice(&quad_vertices),
128 usage: wgpu::BufferUsages::VERTEX,
129 });
130
131 let quad_indices: [u16; 6] = [0, 2, 1, 0, 3, 2];
132 let quad_index_buffer = gpu.create_buffer_init(&wgpu::util::BufferInitDescriptor {
133 label: Some("Simple Rect Quad Index Buffer"),
134 contents: bytemuck::cast_slice(&quad_indices),
135 usage: wgpu::BufferUsages::INDEX,
136 });
137
138 Self {
139 pipeline,
140 bind_group_layout,
141 quad_vertex_buffer,
142 quad_index_buffer,
143 }
144 }
145}
146
147fn build_instances(
148 commands: &[(&SimpleRectCommand, PxSize, PxPosition)],
149 config: &wgpu::SurfaceConfiguration,
150) -> Vec<RectUniform> {
151 commands
152 .iter()
153 .map(|(command, size, position)| RectUniform {
154 position: Vec4::new(
155 position.x.raw() as f32,
156 position.y.raw() as f32,
157 size.width.raw() as f32,
158 size.height.raw() as f32,
159 ),
160 color: Vec4::from_array(command.color.to_array()),
161 screen_size: Vec2::new(config.width as f32, config.height as f32),
162 _padding: [0.0; 2],
163 })
164 .collect()
165}
166
167impl DrawablePipeline<SimpleRectCommand> for SimpleRectPipeline {
168 fn draw(&mut self, context: &mut DrawContext<SimpleRectCommand>) {
169 if context.commands.is_empty() {
170 return;
171 }
172
173 let instances = build_instances(context.commands, context.config);
174 if instances.is_empty() {
175 return;
176 }
177
178 let uniform_buffer = context.device.create_buffer(&wgpu::BufferDescriptor {
179 label: Some("Simple Rect Storage Buffer"),
180 size: 16 + RectUniform::SHADER_SIZE.get() * instances.len() as u64,
181 usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST,
182 mapped_at_creation: false,
183 });
184
185 let uniforms = RectInstances { instances };
186 let mut buffer_content = StorageBuffer::new(Vec::<u8>::new());
187 buffer_content.write(&uniforms).unwrap();
188 context
189 .queue
190 .write_buffer(&uniform_buffer, 0, buffer_content.as_ref());
191
192 let bind_group = context
193 .device
194 .create_bind_group(&wgpu::BindGroupDescriptor {
195 layout: &self.bind_group_layout,
196 entries: &[wgpu::BindGroupEntry {
197 binding: 0,
198 resource: uniform_buffer.as_entire_binding(),
199 }],
200 label: Some("simple_rect_bind_group"),
201 });
202
203 context.render_pass.set_pipeline(&self.pipeline);
204 context.render_pass.set_bind_group(0, &bind_group, &[]);
205 context
206 .render_pass
207 .set_vertex_buffer(0, self.quad_vertex_buffer.slice(..));
208 context
209 .render_pass
210 .set_index_buffer(self.quad_index_buffer.slice(..), wgpu::IndexFormat::Uint16);
211 context
212 .render_pass
213 .draw_indexed(0..6, 0, 0..context.commands.len() as u32);
214 }
215}