1use wgpu::util::DeviceExt;
2use wgpu::{
3 BindGroup, BindGroupLayout, Buffer, BufferUsages, ColorTargetState, Device,
4 PipelineLayoutDescriptor, Queue, RenderPipelineDescriptor, TextureFormat, VertexAttribute,
5 VertexBufferLayout, VertexFormat, VertexStepMode,
6};
7
8use petaplot_core::compute::simd::MinMaxPair;
9use crate::camera::ViewportCamera;
10
11pub struct LineRenderPipeline {
13 pub pipeline: wgpu::RenderPipeline,
14 pub camera_buffer: Buffer,
15 pub camera_bind_group: BindGroup,
16 pub camera_bind_group_layout: BindGroupLayout,
17 pub instance_buffer: Option<Buffer>,
18 pub num_instances: u32,
19}
20
21impl LineRenderPipeline {
22 pub fn new(device: &Device, surface_format: TextureFormat, camera: &ViewportCamera) -> Self {
24 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
25 label: Some("Instanced Line Shader"),
26 source: wgpu::ShaderSource::Wgsl(include_str!("shaders/line.wgsl").into()),
27 });
28
29 let camera_uniform = camera.build_uniform();
30 let camera_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
31 label: Some("Camera Uniform Buffer"),
32 contents: bytemuck::cast_slice(&[camera_uniform]),
33 usage: BufferUsages::UNIFORM | BufferUsages::COPY_DST,
34 });
35
36 let camera_bind_group_layout =
37 device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
38 label: Some("Camera Bind Group Layout"),
39 entries: &[wgpu::BindGroupLayoutEntry {
40 binding: 0,
41 visibility: wgpu::ShaderStages::VERTEX | wgpu::ShaderStages::FRAGMENT,
42 ty: wgpu::BindingType::Buffer {
43 ty: wgpu::BufferBindingType::Uniform,
44 has_dynamic_offset: false,
45 min_binding_size: None,
46 },
47 count: None,
48 }],
49 });
50
51 let camera_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
52 label: Some("Camera Bind Group"),
53 layout: &camera_bind_group_layout,
54 entries: &[wgpu::BindGroupEntry {
55 binding: 0,
56 resource: camera_buffer.as_entire_binding(),
57 }],
58 });
59
60 let instance_layout = VertexBufferLayout {
61 array_stride: (std::mem::size_of::<f32>() * 3) as u64,
62 step_mode: VertexStepMode::Instance,
63 attributes: &[
64 VertexAttribute {
65 offset: 0,
66 shader_location: 0,
67 format: VertexFormat::Float32,
68 },
69 VertexAttribute {
70 offset: 4,
71 shader_location: 1,
72 format: VertexFormat::Float32,
73 },
74 VertexAttribute {
75 offset: 8,
76 shader_location: 2,
77 format: VertexFormat::Float32,
78 },
79 ],
80 };
81
82 let pipeline_layout = device.create_pipeline_layout(&PipelineLayoutDescriptor {
83 label: Some("Line Pipeline Layout"),
84 bind_group_layouts: &[&camera_bind_group_layout],
85 push_constant_ranges: &[],
86 });
87
88 let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor {
89 label: Some("Instanced Line Render Pipeline"),
90 layout: Some(&pipeline_layout),
91 vertex: wgpu::VertexState {
92 module: &shader,
93 entry_point: Some("vs_main"),
94 buffers: &[instance_layout],
95 compilation_options: Default::default(),
96 },
97 fragment: Some(wgpu::FragmentState {
98 module: &shader,
99 entry_point: Some("fs_main"),
100 targets: &[Some(ColorTargetState {
101 format: surface_format,
102 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
103 write_mask: wgpu::ColorWrites::ALL,
104 })],
105 compilation_options: Default::default(),
106 }),
107 primitive: wgpu::PrimitiveState {
108 topology: wgpu::PrimitiveTopology::LineList,
109 strip_index_format: None,
110 front_face: wgpu::FrontFace::Ccw,
111 cull_mode: None,
112 polygon_mode: wgpu::PolygonMode::Fill,
113 unclipped_depth: false,
114 conservative: false,
115 },
116 depth_stencil: None,
117 multisample: wgpu::MultisampleState::default(),
118 multiview: None,
119 cache: None,
120 });
121
122 Self {
123 pipeline,
124 camera_buffer,
125 camera_bind_group,
126 camera_bind_group_layout,
127 instance_buffer: None,
128 num_instances: 0,
129 }
130 }
131
132 pub fn update_camera(&self, queue: &Queue, camera: &ViewportCamera) {
134 let uniform = camera.build_uniform();
135 queue.write_buffer(&self.camera_buffer, 0, bytemuck::cast_slice(&[uniform]));
136 }
137
138 pub fn upload_instances(&mut self, device: &Device, pairs: &[MinMaxPair], x_step: f32) {
140 if pairs.is_empty() {
141 self.num_instances = 0;
142 return;
143 }
144
145 let mut raw_instance_data: Vec<f32> = Vec::with_capacity(pairs.len() * 3);
146 for (i, pair) in pairs.iter().enumerate() {
147 let x_pos = i as f32 * x_step;
148 raw_instance_data.push(x_pos);
149 raw_instance_data.push(pair.min);
150 raw_instance_data.push(pair.max);
151 }
152
153 let buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
154 label: Some("Line Instance Buffer"),
155 contents: bytemuck::cast_slice(&raw_instance_data),
156 usage: BufferUsages::VERTEX | BufferUsages::COPY_DST,
157 });
158
159 self.instance_buffer = Some(buffer);
160 self.num_instances = pairs.len() as u32;
161 }
162
163 pub fn render<'a>(&'a self, render_pass: &mut wgpu::RenderPass<'a>) {
165 if let Some(ref instance_buffer) = self.instance_buffer {
166 if self.num_instances > 0 {
167 render_pass.set_pipeline(&self.pipeline);
168 render_pass.set_bind_group(0, &self.camera_bind_group, &[]);
169 render_pass.set_vertex_buffer(0, instance_buffer.slice(..));
170 render_pass.draw(0..2, 0..self.num_instances);
171 }
172 }
173 }
174}