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 { 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> { 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()) } }