Working compute

This commit is contained in:
2025-12-05 11:01:41 +01:00
parent f6969f77f2
commit f0a0b93db9
6 changed files with 2911 additions and 2 deletions

2564
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -4,3 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
env_logger = "0.11.8"
pollster = "0.4.0"
wgpu = {version = "27.0.1", features = ["spirv"]}
winit = "0.30.12"

3
shaders/build.sh Executable file
View File

@ -0,0 +1,3 @@
#!/bin/sh
glslc compute.comp -o compute.sv

37
shaders/compute.comp Normal file
View File

@ -0,0 +1,37 @@
#version 450
layout (binding = 0, rgba32f) uniform writeonly image2D image;
layout (push_constant) uniform constants
{
uint width;
uint height;
} PushConstants;
layout (local_size_x = 16, local_size_y = 16, local_size_z = 1) in;
void main()
{
vec2 pos = vec2(float(gl_GlobalInvocationID.x) / float(PushConstants.width) - 0.5f, float(gl_GlobalInvocationID.y) / float(PushConstants.height) - 0.5f) * 3.f;
int iter = 0;
float re = 0;
float im = 0;
while(iter < 200)
{
float new_im = pos.y + 2. * re * im;
float new_re = pos.x + (re * re - im * im);
re = new_re;
im = new_im;
if(re * re + im * im > 4.f)
{
float c = float(iter) / 200;
imageStore(image, ivec2(gl_GlobalInvocationID.xy), vec4(c, 0.2, 1.0 - c, 1.0));
return;
}
iter += 1;
}
imageStore(image, ivec2(gl_GlobalInvocationID.xy), vec4(0.f));
}

BIN
shaders/compute.sv Normal file

Binary file not shown.

View File

@ -1,3 +1,304 @@
fn main() {
println!("Hello, world!");
use std::borrow::Cow;
use std::fs::File;
use std::io::Read;
use std::num::NonZero;
use std::sync::Arc;
use wgpu::BindGroupDescriptor;
use wgpu::BindGroupEntry;
use wgpu::BindGroupLayout;
use wgpu::BindGroupLayoutDescriptor;
use wgpu::BindGroupLayoutEntry;
use wgpu::ComputePipeline;
use wgpu::ComputePipelineDescriptor;
use wgpu::Features;
use wgpu::FeaturesWGPU;
use wgpu::FeaturesWebGPU;
use wgpu::Origin3d;
use wgpu::PipelineLayoutDescriptor;
use wgpu::PushConstantRange;
use wgpu::ShaderStages;
use wgpu::TexelCopyTextureInfo;
use wgpu::TextureFormat;
use wgpu::TextureTransition;
use wgpu::TextureUsages;
use wgpu::TextureUses;
use wgpu::util::TextureBlitter;
use winit::application::ApplicationHandler;
use winit::dpi::LogicalSize;
use winit::event::WindowEvent;
use winit::event_loop::EventLoop;
use winit::window::Window;
struct State {
window: Arc<Window>,
device: wgpu::Device,
queue: wgpu::Queue,
size: winit::dpi::PhysicalSize<u32>,
surface: wgpu::Surface<'static>,
surface_format: wgpu::TextureFormat,
compute_target: Option<wgpu::Texture>,
compute_pipe: ComputePipeline,
compute_layout: BindGroupLayout,
}
struct App(Option<State>);
impl App {
pub fn new() -> App {
App(None)
}
}
impl State {
async fn new(window: Arc<Window>) -> Self {
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions::default())
.await
.unwrap();
println!("Adapter : {:#?}", adapter.get_info());
let (device, queue) = adapter
.request_device(&wgpu::wgt::DeviceDescriptor {
required_features: Features {
features_wgpu: FeaturesWGPU::TEXTURE_BINDING_ARRAY
| FeaturesWGPU::STORAGE_RESOURCE_BINDING_ARRAY
| FeaturesWGPU::PUSH_CONSTANTS,
features_webgpu: FeaturesWebGPU::default(),
},
required_limits: wgpu::Limits {
max_binding_array_elements_per_shader_stage: 16,
max_push_constant_size: 16,
..Default::default()
},
..Default::default()
})
.await
.unwrap();
let size = window.inner_size();
let surface = instance.create_surface(window.clone()).unwrap();
let cap = surface.get_capabilities(&adapter);
let surface_format = cap.formats[0];
let mut shader_source = Vec::new();
File::open("shaders/compute.sv")
.unwrap()
.read_to_end(&mut shader_source)
.unwrap();
let shader_source = shader_source
.chunks(4)
.map(|x| u32::from_ne_bytes([x[0], x[1], x[2], x[3]]))
.collect::<Vec<u32>>();
let compute_layout = device.create_bind_group_layout(&BindGroupLayoutDescriptor {
label: None,
entries: &[BindGroupLayoutEntry {
binding: 0,
ty: wgpu::BindingType::StorageTexture {
access: wgpu::StorageTextureAccess::WriteOnly,
format: TextureFormat::Rgba32Float,
view_dimension: wgpu::TextureViewDimension::D2,
},
visibility: ShaderStages::COMPUTE,
count: NonZero::new(1u32),
}],
});
let compute_pipe = device.create_compute_pipeline(&ComputePipelineDescriptor {
label: None,
layout: Some(&device.create_pipeline_layout(&PipelineLayoutDescriptor {
label: None,
bind_group_layouts: &[&compute_layout],
push_constant_ranges: &[PushConstantRange {
stages: ShaderStages::COMPUTE,
range: 0..(2 * size_of::<u32>() as u32),
}],
})),
module: &device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: None,
source: wgpu::ShaderSource::SpirV(Cow::from(shader_source.as_slice())),
}),
entry_point: Some("main"),
compilation_options: Default::default(),
cache: None,
});
let mut state = State {
window,
device,
queue,
size,
surface,
surface_format,
compute_layout,
compute_pipe,
compute_target: None,
};
state.configure_surface();
state
}
fn resize(&mut self, new_size: winit::dpi::PhysicalSize<u32>) {
self.size = new_size;
self.configure_surface();
}
fn configure_surface(&mut self) {
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: self.surface_format,
view_formats: vec![self.surface_format.add_srgb_suffix()],
alpha_mode: wgpu::CompositeAlphaMode::Auto,
width: self.size.width,
height: self.size.height,
desired_maximum_frame_latency: 1,
present_mode: wgpu::PresentMode::AutoVsync,
};
self.surface.configure(&self.device, &surface_config);
self.compute_target
.replace(self.device.create_texture(&wgpu::wgt::TextureDescriptor {
label: None,
size: wgpu::Extent3d {
width: self.size.width,
height: self.size.height,
..Default::default()
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: TextureFormat::Rgba32Float.add_srgb_suffix(),
usage: TextureUsages::STORAGE_BINDING | TextureUsages::TEXTURE_BINDING,
view_formats: &[TextureFormat::Rgba32Float.add_srgb_suffix()],
}));
}
fn render(&mut self) {
let surface_texture = self
.surface
.get_current_texture()
.expect("Could not get image from swapchain.");
// let texture_view = surface_texture
// .texture
// .create_view(&wgpu::wgt::TextureViewDescriptor {
// format: Some(self.surface_format.add_srgb_suffix()),
// ..Default::default()
// });
//
let mut encoder = self.device.create_command_encoder(&Default::default());
let mut compute_pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
label: None,
timestamp_writes: None,
});
compute_pass.set_pipeline(&self.compute_pipe);
let push_constants = [self.size.width, self.size.height]
.iter()
.flat_map(|x| x.to_ne_bytes())
.collect::<Vec<u8>>();
compute_pass.set_push_constants(0, push_constants.as_slice());
compute_pass.set_bind_group(
0,
Some(&self.device.create_bind_group(&BindGroupDescriptor {
label: None,
layout: &self.compute_layout,
entries: &[BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(
&self.compute_target.as_mut().unwrap().create_view(
&wgpu::wgt::TextureViewDescriptor {
format: Some(TextureFormat::Rgba32Float.add_srgb_suffix()),
..Default::default()
},
),
),
}],
})),
&[],
);
compute_pass.dispatch_workgroups(120, 68, 1);
drop(compute_pass);
TextureBlitter::new(&self.device, self.surface_format).copy(
&self.device,
&mut encoder,
&self
.compute_target
.as_mut()
.unwrap()
.create_view(&wgpu::wgt::TextureViewDescriptor {
format: Some(TextureFormat::Rgba32Float.add_srgb_suffix()),
..Default::default()
}),
&surface_texture
.texture
.create_view(&wgpu::wgt::TextureViewDescriptor {
format: Some(self.surface_format),
..Default::default()
}),
);
self.queue.submit([encoder.finish()]);
self.window.pre_present_notify();
surface_texture.present();
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) {
let window = Arc::new(
event_loop
.create_window(
Window::default_attributes()
.with_maximized(true)
.with_resizable(true)
.with_title("R-Brot")
.with_inner_size(LogicalSize::new(1920, 1080)),
)
.unwrap(),
);
let state = pollster::block_on(State::new(window.clone()));
self.0.replace(state);
window.request_redraw();
}
fn window_event(
&mut self,
event_loop: &winit::event_loop::ActiveEventLoop,
window_id: winit::window::WindowId,
event: winit::event::WindowEvent,
) {
let state = self.0.as_mut().unwrap();
match event {
WindowEvent::Resized(new_size) => state.resize(new_size),
WindowEvent::RedrawRequested => {
state.render();
state.window.request_redraw();
}
WindowEvent::CloseRequested => {
event_loop.exit();
}
_ => {}
}
}
}
fn main() {
env_logger::init();
let event_loop = EventLoop::new().unwrap();
event_loop.set_control_flow(winit::event_loop::ControlFlow::Poll);
let mut app = App::new();
event_loop.run_app(&mut app).unwrap();
}