1use crate::core::renderer::Vertex;
2use crate::core::scene::GpuVertexBuffer;
3use crate::gpu::shaders;
4use crate::gpu::{tuning, ScalarType};
5use glam::Vec4;
6use std::sync::Arc;
7use wgpu::util::DeviceExt;
8const VERTICES_PER_BAR: u32 = 6;
9
10pub struct BarGpuInputs {
12 pub values_buffer: Arc<wgpu::Buffer>,
13 pub row_count: u32,
14 pub scalar: ScalarType,
15}
16
17pub struct BarGpuParams {
19 pub color: Vec4,
20 pub bar_width: f32,
21 pub series_index: u32,
22 pub series_count: u32,
23 pub group_index: u32,
24 pub group_count: u32,
25 pub orientation: BarOrientation,
26 pub layout: BarLayoutMode,
27}
28
29#[derive(Clone, Copy, Debug)]
30pub enum BarLayoutMode {
31 Grouped,
32 Stacked,
33}
34
35impl BarLayoutMode {
36 fn as_u32(self) -> u32 {
37 match self {
38 BarLayoutMode::Grouped => 0,
39 BarLayoutMode::Stacked => 1,
40 }
41 }
42}
43
44#[derive(Clone, Copy, Debug)]
45pub enum BarOrientation {
46 Vertical,
47 Horizontal,
48}
49
50impl BarOrientation {
51 fn as_u32(self) -> u32 {
52 match self {
53 BarOrientation::Vertical => 0,
54 BarOrientation::Horizontal => 1,
55 }
56 }
57}
58
59#[repr(C)]
60#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
61struct BarUniforms {
62 color: [f32; 4],
63 bar_width: f32,
64 row_count: u32,
65 series_index: u32,
66 series_count: u32,
67 group_index: u32,
68 group_count: u32,
69 orientation: u32,
70 layout: u32,
71 _pad: [u32; 2],
72}
73
74pub fn pack_vertices_from_values(
76 device: &Arc<wgpu::Device>,
77 queue: &Arc<wgpu::Queue>,
78 inputs: &BarGpuInputs,
79 params: &BarGpuParams,
80) -> Result<GpuVertexBuffer, String> {
81 if inputs.row_count == 0 {
82 return Err("bar: input cannot be empty".to_string());
83 }
84
85 let workgroup_size = tuning::effective_workgroup_size();
86 let shader = compile_shader(device, workgroup_size, inputs.scalar);
87
88 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
89 label: Some("bar-pack-bind-layout"),
90 entries: &[
91 wgpu::BindGroupLayoutEntry {
92 binding: 0,
93 visibility: wgpu::ShaderStages::COMPUTE,
94 ty: wgpu::BindingType::Buffer {
95 ty: wgpu::BufferBindingType::Storage { read_only: true },
96 has_dynamic_offset: false,
97 min_binding_size: None,
98 },
99 count: None,
100 },
101 wgpu::BindGroupLayoutEntry {
102 binding: 1,
103 visibility: wgpu::ShaderStages::COMPUTE,
104 ty: wgpu::BindingType::Buffer {
105 ty: wgpu::BufferBindingType::Storage { read_only: false },
106 has_dynamic_offset: false,
107 min_binding_size: None,
108 },
109 count: None,
110 },
111 wgpu::BindGroupLayoutEntry {
112 binding: 2,
113 visibility: wgpu::ShaderStages::COMPUTE,
114 ty: wgpu::BindingType::Buffer {
115 ty: wgpu::BufferBindingType::Uniform,
116 has_dynamic_offset: false,
117 min_binding_size: None,
118 },
119 count: None,
120 },
121 ],
122 });
123
124 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
125 label: Some("bar-pack-pipeline-layout"),
126 bind_group_layouts: &[&bind_group_layout],
127 push_constant_ranges: &[],
128 });
129
130 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
131 label: Some("bar-pack-pipeline"),
132 layout: Some(&pipeline_layout),
133 module: &shader,
134 entry_point: "main",
135 });
136
137 let vertex_count = inputs.row_count as u64 * VERTICES_PER_BAR as u64;
138 let output_size = vertex_count * std::mem::size_of::<Vertex>() as u64;
139 let output_buffer = Arc::new(device.create_buffer(&wgpu::BufferDescriptor {
140 label: Some("bar-gpu-vertices"),
141 size: output_size,
142 usage: wgpu::BufferUsages::STORAGE
143 | wgpu::BufferUsages::VERTEX
144 | wgpu::BufferUsages::COPY_DST,
145 mapped_at_creation: false,
146 }));
147
148 let uniforms = BarUniforms {
149 color: params.color.to_array(),
150 bar_width: params.bar_width,
151 row_count: inputs.row_count,
152 series_index: params.series_index,
153 series_count: params.series_count.max(1),
154 group_index: params.group_index,
155 group_count: params.group_count.max(1),
156 orientation: params.orientation.as_u32(),
157 layout: params.layout.as_u32(),
158 _pad: [0, 0],
159 };
160 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
161 label: Some("bar-pack-uniforms"),
162 contents: bytemuck::bytes_of(&uniforms),
163 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
164 });
165
166 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
167 label: Some("bar-pack-bind-group"),
168 layout: &bind_group_layout,
169 entries: &[
170 wgpu::BindGroupEntry {
171 binding: 0,
172 resource: inputs.values_buffer.as_entire_binding(),
173 },
174 wgpu::BindGroupEntry {
175 binding: 1,
176 resource: output_buffer.as_entire_binding(),
177 },
178 wgpu::BindGroupEntry {
179 binding: 2,
180 resource: uniform_buffer.as_entire_binding(),
181 },
182 ],
183 });
184
185 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
186 label: Some("bar-pack-encoder"),
187 });
188 {
189 let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
190 label: Some("bar-pack-pass"),
191 timestamp_writes: None,
192 });
193 pass.set_pipeline(&pipeline);
194 pass.set_bind_group(0, &bind_group, &[]);
195 let workgroups = inputs.row_count.div_ceil(workgroup_size);
196 pass.dispatch_workgroups(workgroups, 1, 1);
197 }
198 queue.submit(Some(encoder.finish()));
199
200 Ok(GpuVertexBuffer::new(output_buffer, vertex_count as usize))
201}
202
203fn compile_shader(
204 device: &Arc<wgpu::Device>,
205 workgroup_size: u32,
206 scalar: ScalarType,
207) -> wgpu::ShaderModule {
208 let template = match scalar {
209 ScalarType::F32 => shaders::bar::F32,
210 ScalarType::F64 => shaders::bar::F64,
211 };
212 let source = template.replace("{{WORKGROUP_SIZE}}", &workgroup_size.to_string());
213 device.create_shader_module(wgpu::ShaderModuleDescriptor {
214 label: Some("bar-pack-shader"),
215 source: wgpu::ShaderSource::Wgsl(source.into()),
216 })
217}