1use crate::data::{ChartData, Vertex};
2use crate::renderer::{Renderer, WindowRenderer, WebRenderer, RenderOptions};
3use crate::backend::GPUBackend;
4use crate::shaders::{SIMPLE_VERTEX_SHADER, SIMPLE_FRAGMENT_SHADER};
5use wgpu::util::DeviceExt;
6
7pub struct ScatterRenderer {
18 render_pipeline: wgpu::RenderPipeline,
19 vertex_buffer: Option<wgpu::Buffer>,
20 vertex_count: u32,
21}
22
23impl Renderer for ScatterRenderer {
28 fn render_to_pass<'rpass>(&'rpass mut self, render_pass: &mut wgpu::RenderPass<'rpass>) {
29 render_pass.set_pipeline(&self.render_pipeline);
30
31 if let Some(ref buffer) = self.vertex_buffer {
32 render_pass.set_vertex_buffer(0, buffer.slice(..));
33 render_pass.draw(0..self.vertex_count, 0..1);
34 }
35 }
36}
37
38impl WindowRenderer for ScatterRenderer {
43 fn new(
45 device: &wgpu::Device,
46 config: &wgpu::SurfaceConfiguration,
47 chart_data: ChartData,
48 ) -> Self {
49 let vertex_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
51 label: Some("Scatter Vertex Shader"),
52 source: wgpu::ShaderSource::Wgsl(SIMPLE_VERTEX_SHADER.into()),
53 });
54
55 let fragment_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
56 label: Some("Scatter Fragment Shader"),
57 source: wgpu::ShaderSource::Wgsl(SIMPLE_FRAGMENT_SHADER.into()),
58 });
59
60 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
62 label: Some("Scatter Pipeline Layout"),
63 bind_group_layouts: &[],
64 push_constant_ranges: &[],
65 });
66
67 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
69 label: Some("Scatter Render Pipeline"),
70 layout: Some(&pipeline_layout),
71 vertex: wgpu::VertexState {
72 module: &vertex_shader,
73 entry_point: "vs_main",
74 buffers: &[Vertex::desc()],
75 compilation_options: Default::default(),
76 },
77 fragment: Some(wgpu::FragmentState {
78 module: &fragment_shader,
79 entry_point: "fs_main",
80 targets: &[Some(wgpu::ColorTargetState {
81 format: config.format, blend: Some(wgpu::BlendState::ALPHA_BLENDING),
83 write_mask: wgpu::ColorWrites::ALL,
84 })],
85 compilation_options: Default::default(),
86 }),
87 primitive: wgpu::PrimitiveState {
88 topology: wgpu::PrimitiveTopology::PointList,
89 strip_index_format: None,
90 front_face: wgpu::FrontFace::Ccw,
91 cull_mode: None,
92 polygon_mode: wgpu::PolygonMode::Fill,
93 unclipped_depth: false,
94 conservative: false,
95 },
96 depth_stencil: None,
97 multisample: wgpu::MultisampleState {
98 count: 1,
99 mask: !0,
100 alpha_to_coverage_enabled: false,
101 },
102 multiview: None,
103 cache: None,
104 });
105
106 let vertices = &chart_data.vertices;
108 let vertex_buffer = if !vertices.is_empty() {
109 Some(device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
110 label: Some("Scatter Vertex Buffer"),
111 contents: bytemuck::cast_slice(&vertices),
112 usage: wgpu::BufferUsages::VERTEX,
113 }))
114 } else {
115 None
116 };
117
118 ScatterRenderer {
119 render_pipeline,
120 vertex_buffer,
121 vertex_count: vertices.len() as u32,
122 }
123 }
124
125 fn update_data(&mut self, device: &wgpu::Device, chart_data: &ChartData) {
127 let vertices = &chart_data.vertices;
128
129 if !vertices.is_empty() {
130 self.vertex_buffer = Some(device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
131 label: Some("Scatter Vertex Buffer"),
132 contents: bytemuck::cast_slice(&vertices),
133 usage: wgpu::BufferUsages::VERTEX,
134 }));
135 self.vertex_count = vertices.len() as u32;
136 } else {
137 self.vertex_buffer = None;
138 self.vertex_count = 0;
139 }
140 }
141}
142
143impl WebRenderer for ScatterRenderer {
148 fn new(backend: &GPUBackend) -> Result<Self, String> {
149 let device = backend.device()?;
150 let config = backend.config.as_ref().ok_or("Backend not configured")?;
151
152 let vertex_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
154 label: Some("Scatter Vertex Shader"),
155 source: wgpu::ShaderSource::Wgsl(SIMPLE_VERTEX_SHADER.into()),
156 });
157
158 let fragment_shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
159 label: Some("Scatter Fragment Shader"),
160 source: wgpu::ShaderSource::Wgsl(SIMPLE_FRAGMENT_SHADER.into()),
161 });
162
163 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
164 label: Some("Scatter Pipeline Layout"),
165 bind_group_layouts: &[],
166 push_constant_ranges: &[],
167 });
168
169 let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
170 label: Some("Scatter Render Pipeline"),
171 layout: Some(&pipeline_layout),
172 vertex: wgpu::VertexState {
173 module: &vertex_shader,
174 entry_point: "vs_main",
175 buffers: &[Vertex::desc()],
176 compilation_options: Default::default(),
177 },
178 fragment: Some(wgpu::FragmentState {
179 module: &fragment_shader,
180 entry_point: "fs_main",
181 targets: &[Some(wgpu::ColorTargetState {
182 format: config.format,
183 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
184 write_mask: wgpu::ColorWrites::ALL,
185 })],
186 compilation_options: Default::default(),
187 }),
188 primitive: wgpu::PrimitiveState {
189 topology: wgpu::PrimitiveTopology::PointList,
190 strip_index_format: None,
191 front_face: wgpu::FrontFace::Ccw,
192 cull_mode: None,
193 polygon_mode: wgpu::PolygonMode::Fill,
194 unclipped_depth: false,
195 conservative: false,
196 },
197 depth_stencil: None,
198 multisample: wgpu::MultisampleState {
199 count: 1,
200 mask: !0,
201 alpha_to_coverage_enabled: false,
202 },
203 multiview: None,
204 cache: None,
205 });
206
207 Ok(ScatterRenderer {
208 render_pipeline,
209 vertex_buffer: None,
210 vertex_count: 0,
211 })
212 }
213
214 fn render_with_backend(
215 &mut self,
216 backend: &GPUBackend,
217 data: &ChartData,
218 options: &RenderOptions,
219 ) -> Result<(), String> {
220 <Self as WebRenderer>::update_data(self, backend, data)?;
222
223 let device = backend.device()?;
224 let queue = backend.queue()?;
225 let surface = backend.surface.as_ref().ok_or("Surface not configured")?;
226
227 let frame = surface
229 .get_current_texture()
230 .map_err(|e| format!("Failed to get current texture: {}", e))?;
231
232 let view = frame
233 .texture
234 .create_view(&wgpu::TextureViewDescriptor::default());
235
236 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
238 label: Some("Render Encoder"),
239 });
240
241 {
243 let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
244 label: Some("Render Pass"),
245 color_attachments: &[Some(wgpu::RenderPassColorAttachment {
246 view: &view,
247 resolve_target: None,
248 ops: wgpu::Operations {
249 load: wgpu::LoadOp::Clear(options.clear_color),
250 store: wgpu::StoreOp::Store,
251 },
252 })],
253 depth_stencil_attachment: None,
254 timestamp_writes: None,
255 occlusion_query_set: None,
256 });
257
258 self.render_to_pass(&mut render_pass);
259 }
260
261 queue.submit(std::iter::once(encoder.finish()));
263 frame.present();
264
265 Ok(())
266 }
267
268 fn update_data(&mut self, backend: &GPUBackend, data: &ChartData) -> Result<(), String> {
269 if data.vertices.is_empty() {
270 return Ok(());
271 }
272
273 let device = backend.device()?;
274
275 self.vertex_buffer = Some(device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
277 label: Some("Vertex Buffer"),
278 contents: bytemuck::cast_slice(&data.vertices),
279 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
280 }));
281 self.vertex_count = data.vertices.len() as u32;
282
283 Ok(())
284 }
285}