wgpu_examples/render_with_compute/
mod.rs1use std::time::Instant;
7
8#[derive(bytemuck::Pod, bytemuck::Zeroable, Clone, Copy, Debug)]
9#[repr(C)]
10#[repr(align(16))]
11struct GlobalParams {
12 time: f32,
13 frame: f32,
14 _padding: [u8; 8],
15}
16
17pub struct Example {
18 pipeline: wgpu::ComputePipeline,
19 texture_view: wgpu::TextureView,
20 global_params: wgpu::Buffer,
21 bg: wgpu::BindGroup,
22 bgl: wgpu::BindGroupLayout,
23 blitter: wgpu::util::TextureBlitter,
24 frame_count: u32,
25 start_time: Option<Instant>,
26}
27impl crate::framework::Example for Example {
28 fn init(
29 config: &wgpu::SurfaceConfiguration,
30 _adapter: &wgpu::Adapter,
31 device: &wgpu::Device,
32 _queue: &wgpu::Queue,
33 ) -> Self {
34 let sm = device.create_shader_module(wgpu::include_wgsl!("shader.wgsl"));
35 let bgl = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
36 label: None,
37 entries: &[
38 wgpu::BindGroupLayoutEntry {
39 binding: 0,
40 visibility: wgpu::ShaderStages::COMPUTE,
41 ty: wgpu::BindingType::Buffer {
42 ty: wgpu::BufferBindingType::Uniform,
43 has_dynamic_offset: false,
44 min_binding_size: None,
45 },
46 count: None,
47 },
48 wgpu::BindGroupLayoutEntry {
49 binding: 1,
50 visibility: wgpu::ShaderStages::COMPUTE,
51 ty: wgpu::BindingType::StorageTexture {
52 access: wgpu::StorageTextureAccess::WriteOnly,
53 format: wgpu::TextureFormat::Rgba8Unorm,
54 view_dimension: wgpu::TextureViewDimension::D2,
55 },
56 count: None,
57 },
58 ],
59 });
60 let ppl = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
61 label: None,
62 bind_group_layouts: &[Some(&bgl)],
63 immediate_size: 0,
64 });
65 let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor {
66 label: None,
67 layout: Some(&ppl),
68 module: &sm,
69 entry_point: None,
70 compilation_options: Default::default(),
71 cache: None,
72 });
73 let blitter = wgpu::util::TextureBlitter::new(device, config.format);
74 let global_params = device.create_buffer(&wgpu::BufferDescriptor {
75 label: None,
76 size: size_of::<GlobalParams>() as u64,
77 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
78 mapped_at_creation: false,
79 });
80
81 let (texture_view, bg) =
82 create_tv_and_bg(device, &bgl, &global_params, config.width, config.height);
83 Self {
84 pipeline,
85 texture_view,
86 global_params,
87 bg,
88 bgl,
89 blitter,
90 frame_count: 0,
91 start_time: None,
92 }
93 }
94
95 fn required_limits() -> wgpu::Limits {
96 wgpu::Limits {
97 max_storage_textures_per_shader_stage: 1,
98 ..Default::default()
99 }
100 }
101
102 fn resize(
103 &mut self,
104 config: &wgpu::SurfaceConfiguration,
105 device: &wgpu::Device,
106 _queue: &wgpu::Queue,
107 ) {
108 let (texture_view, bg) = create_tv_and_bg(
109 device,
110 &self.bgl,
111 &self.global_params,
112 config.width,
113 config.height,
114 );
115 self.bg = bg;
116 self.texture_view = texture_view;
117 }
118
119 fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) {
120 let now = Instant::now();
121 let start = *self.start_time.get_or_insert(now);
122 let time_since_start = (now - start).as_secs_f32();
123 queue.write_buffer(
124 &self.global_params,
125 0,
126 bytemuck::bytes_of(&GlobalParams {
127 time: time_since_start,
128 frame: self.frame_count as f32,
129 _padding: [0; 8],
130 }),
131 );
132 let mut encoder = device.create_command_encoder(&Default::default());
133
134 {
135 let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor {
136 label: None,
137 timestamp_writes: None,
138 });
139 pass.set_pipeline(&self.pipeline);
140 pass.set_bind_group(0, &self.bg, &[]);
141 const SHADER_WORKGROUP_DIM: u32 = 16;
142 pass.dispatch_workgroups(
143 self.texture_view
144 .texture()
145 .width()
146 .div_ceil(SHADER_WORKGROUP_DIM),
147 self.texture_view
148 .texture()
149 .height()
150 .div_ceil(SHADER_WORKGROUP_DIM),
151 1,
152 );
153 }
154 self.blitter
155 .copy(device, &mut encoder, &self.texture_view, view);
156
157 queue.submit([encoder.finish()]);
158 device.poll(wgpu::PollType::wait_indefinitely()).unwrap();
159
160 self.frame_count += 1;
161 }
162
163 fn update(&mut self, _event: winit::event::WindowEvent) {}
164}
165
166fn create_tv_and_bg(
167 device: &wgpu::Device,
168 bgl: &wgpu::BindGroupLayout,
169 global_params: &wgpu::Buffer,
170 width: u32,
171 height: u32,
172) -> (wgpu::TextureView, wgpu::BindGroup) {
173 let texture = device.create_texture(&wgpu::TextureDescriptor {
174 label: None,
175 size: wgpu::Extent3d {
176 width,
177 height,
178 depth_or_array_layers: 1,
179 },
180 mip_level_count: 1,
181 sample_count: 1,
182 dimension: wgpu::TextureDimension::D2,
183 format: wgpu::TextureFormat::Rgba8Unorm,
184 usage: wgpu::TextureUsages::STORAGE_BINDING | wgpu::TextureUsages::TEXTURE_BINDING,
185 view_formats: &[],
186 });
187 let view = texture.create_view(&Default::default());
188 let bg = device.create_bind_group(&wgpu::BindGroupDescriptor {
189 label: None,
190 layout: bgl,
191 entries: &[
192 wgpu::BindGroupEntry {
193 binding: 0,
194 resource: wgpu::BindingResource::Buffer(global_params.as_entire_buffer_binding()),
195 },
196 wgpu::BindGroupEntry {
197 binding: 1,
198 resource: wgpu::BindingResource::TextureView(&view),
199 },
200 ],
201 });
202 (view, bg)
203}
204
205pub fn main() {
206 crate::framework::run::<Example>("render-with-compute");
207}