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 =
131 device.create_compute_pipeline(&crate::wgpu_compat::wgpu_compute_pipeline_descriptor! {
132 label: Some("bar-pack-pipeline"),
133 layout: Some(&pipeline_layout),
134 module: &shader,
135 entry_point: "main",
136 });
137
138 let vertex_count = inputs.row_count as u64 * VERTICES_PER_BAR as u64;
139 let output_size = vertex_count * std::mem::size_of::<Vertex>() as u64;
140 let output_buffer = Arc::new(device.create_buffer(&wgpu::BufferDescriptor {
141 label: Some("bar-gpu-vertices"),
142 size: output_size,
143 usage: wgpu::BufferUsages::STORAGE
144 | wgpu::BufferUsages::VERTEX
145 | wgpu::BufferUsages::COPY_DST,
146 mapped_at_creation: false,
147 }));
148
149 let uniforms = BarUniforms {
150 color: params.color.to_array(),
151 bar_width: params.bar_width,
152 row_count: inputs.row_count,
153 series_index: params.series_index,
154 series_count: params.series_count.max(1),
155 group_index: params.group_index,
156 group_count: params.group_count.max(1),
157 orientation: params.orientation.as_u32(),
158 layout: params.layout.as_u32(),
159 _pad: [0, 0],
160 };
161 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
162 label: Some("bar-pack-uniforms"),
163 contents: bytemuck::bytes_of(&uniforms),
164 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
165 });
166
167 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
168 label: Some("bar-pack-bind-group"),
169 layout: &bind_group_layout,
170 entries: &[
171 wgpu::BindGroupEntry {
172 binding: 0,
173 resource: inputs.values_buffer.as_entire_binding(),
174 },
175 wgpu::BindGroupEntry {
176 binding: 1,
177 resource: output_buffer.as_entire_binding(),
178 },
179 wgpu::BindGroupEntry {
180 binding: 2,
181 resource: uniform_buffer.as_entire_binding(),
182 },
183 ],
184 });
185
186 let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor {
187 label: Some("bar-pack-encoder"),
188 });
189 {
190 let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
191 label: Some("bar-pack-pass"),
192 timestamp_writes: None,
193 });
194 pass.set_pipeline(&pipeline);
195 pass.set_bind_group(0, &bind_group, &[]);
196 let workgroups = inputs.row_count.div_ceil(workgroup_size);
197 pass.dispatch_workgroups(workgroups, 1, 1);
198 }
199 queue.submit(Some(encoder.finish()));
200
201 Ok(GpuVertexBuffer::new(output_buffer, vertex_count as usize))
202}
203
204fn compile_shader(
205 device: &Arc<wgpu::Device>,
206 workgroup_size: u32,
207 scalar: ScalarType,
208) -> wgpu::ShaderModule {
209 let template = match scalar {
210 ScalarType::F32 => shaders::bar::F32,
211 ScalarType::F64 => shaders::bar::F64,
212 };
213 let source = template.replace("{{WORKGROUP_SIZE}}", &workgroup_size.to_string());
214 device.create_shader_module(wgpu::ShaderModuleDescriptor {
215 label: Some("bar-pack-shader"),
216 source: wgpu::ShaderSource::Wgsl(source.into()),
217 })
218}