Research-Stack/5-Applications/parquet_compressor/src/gpu.rs
2026-05-11 23:48:42 -05:00

168 lines
6.2 KiB
Rust

use std::borrow::Cow;
use wgpu::util::DeviceExt;
pub struct GpuContext {
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub pipeline: wgpu::ComputePipeline,
pub bind_group_layout: wgpu::BindGroupLayout,
}
impl GpuContext {
pub async fn new() -> anyhow::Result<Self> {
let instance = wgpu::Instance::default();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: wgpu::PowerPreference::HighPerformance,
compatible_surface: None,
force_fallback_adapter: false,
})
.await
.ok_or_else(|| anyhow::anyhow!("No adapter found"))?;
let mut limits = wgpu::Limits::default();
limits.max_storage_buffer_binding_size = adapter.limits().max_storage_buffer_binding_size;
limits.max_buffer_size = adapter.limits().max_buffer_size;
limits.max_compute_invocations_per_workgroup =
adapter.limits().max_compute_invocations_per_workgroup;
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: wgpu::Features::empty(),
required_limits: limits,
},
None,
)
.await?;
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("XOR Shader"),
source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shaders/xor.wgsl"))),
});
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: None,
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: true },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::COMPUTE,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Storage { read_only: false },
has_dynamic_offset: false,
min_binding_size: None,
},
count: None,
},
],
});
let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
});
let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
label: None,
layout: Some(&pipeline_layout),
module: &shader,
entry_point: "main",
});
Ok(Self {
device,
queue,
pipeline,
bind_group_layout,
})
}
pub fn run_xor_transform(&self, data: &[u8], _key: u8) -> anyhow::Result<Vec<u8>> {
let padded_len = (data.len() + 3) & !3;
let mut padded_data = vec![0u8; padded_len];
padded_data[..data.len()].copy_from_slice(data);
let input_buffer = self
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Input Buffer"),
contents: bytemuck::cast_slice(&padded_data),
usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC,
});
let output_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Output Buffer"),
size: padded_len as u64,
usage: wgpu::BufferUsages::STORAGE
| wgpu::BufferUsages::COPY_SRC
| wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let staging_buffer = self.device.create_buffer(&wgpu::BufferDescriptor {
label: Some("Staging Buffer"),
size: padded_len as u64,
usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST,
mapped_at_creation: false,
});
let bind_group = self.device.create_bind_group(&wgpu::BindGroupDescriptor {
label: None,
layout: &self.bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: input_buffer.as_entire_binding(),
},
wgpu::BindGroupEntry {
binding: 1,
resource: output_buffer.as_entire_binding(),
},
],
});
let mut encoder = self
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: None,
timestamp_writes: None,
});
compute_pass.set_pipeline(&self.pipeline);
compute_pass.set_bind_group(0, &bind_group, &[]);
let total_elements = (padded_len / 4) as u32;
let workgroups_x = ((total_elements + 255) / 256).min(65535);
let workgroups_y = (total_elements + 256 * 65535 - 1) / (256 * 65535);
compute_pass.dispatch_workgroups(workgroups_x, workgroups_y.max(1), 1);
}
encoder.copy_buffer_to_buffer(&output_buffer, 0, &staging_buffer, 0, padded_len as u64);
self.queue.submit(Some(encoder.finish()));
let (sender, receiver) = std::sync::mpsc::channel();
let buffer_slice = staging_buffer.slice(..);
buffer_slice.map_async(wgpu::MapMode::Read, move |v| sender.send(v).unwrap());
self.device.poll(wgpu::Maintain::Wait);
receiver.recv()??;
let data_bytes = buffer_slice.get_mapped_range().to_vec();
staging_buffer.unmap();
Ok(data_bytes[..data.len()].to_vec())
}
}