Working compute
This commit is contained in:
2564
Cargo.lock
generated
Normal file
2564
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
3
shaders/build.sh
Executable file
@ -0,0 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
glslc compute.comp -o compute.sv
|
||||
37
shaders/compute.comp
Normal file
37
shaders/compute.comp
Normal 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
BIN
shaders/compute.sv
Normal file
Binary file not shown.
305
src/main.rs
305
src/main.rs
@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user