1#![cfg(not(target_arch = "wasm32"))]
4#![warn(clippy::allow_attributes, unsafe_op_in_unsafe_fn)]
5
6extern crate wgpu_core as wgc;
7extern crate wgpu_types as wgt;
8
9use wgc::{device::trace, identity::IdentityManager};
10
11use std::{borrow::Cow, fs, path::Path};
12
13pub trait GlobalPlay {
14 fn encode_commands(
15 &self,
16 encoder: wgc::id::CommandEncoderId,
17 commands: Vec<trace::Command>,
18 command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
19 ) -> wgc::id::CommandBufferId;
20 fn process(
21 &self,
22 device: wgc::id::DeviceId,
23 queue: wgc::id::QueueId,
24 action: trace::Action,
25 dir: &Path,
26 command_encoder_id_manager: &mut IdentityManager<wgc::id::markers::CommandEncoder>,
27 command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
28 );
29}
30
31impl GlobalPlay for wgc::global::Global {
32 fn encode_commands(
33 &self,
34 encoder: wgc::id::CommandEncoderId,
35 commands: Vec<trace::Command>,
36 command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
37 ) -> wgc::id::CommandBufferId {
38 for command in commands {
39 match command {
40 trace::Command::CopyBufferToBuffer {
41 src,
42 src_offset,
43 dst,
44 dst_offset,
45 size,
46 } => self
47 .command_encoder_copy_buffer_to_buffer(
48 encoder, src, src_offset, dst, dst_offset, size,
49 )
50 .unwrap(),
51 trace::Command::CopyBufferToTexture { src, dst, size } => self
52 .command_encoder_copy_buffer_to_texture(encoder, &src, &dst, &size)
53 .unwrap(),
54 trace::Command::CopyTextureToBuffer { src, dst, size } => self
55 .command_encoder_copy_texture_to_buffer(encoder, &src, &dst, &size)
56 .unwrap(),
57 trace::Command::CopyTextureToTexture { src, dst, size } => self
58 .command_encoder_copy_texture_to_texture(encoder, &src, &dst, &size)
59 .unwrap(),
60 trace::Command::ClearBuffer { dst, offset, size } => self
61 .command_encoder_clear_buffer(encoder, dst, offset, size)
62 .unwrap(),
63 trace::Command::ClearTexture {
64 dst,
65 subresource_range,
66 } => self
67 .command_encoder_clear_texture(encoder, dst, &subresource_range)
68 .unwrap(),
69 trace::Command::WriteTimestamp {
70 query_set_id,
71 query_index,
72 } => self
73 .command_encoder_write_timestamp(encoder, query_set_id, query_index)
74 .unwrap(),
75 trace::Command::ResolveQuerySet {
76 query_set_id,
77 start_query,
78 query_count,
79 destination,
80 destination_offset,
81 } => self
82 .command_encoder_resolve_query_set(
83 encoder,
84 query_set_id,
85 start_query,
86 query_count,
87 destination,
88 destination_offset,
89 )
90 .unwrap(),
91 trace::Command::PushDebugGroup(marker) => self
92 .command_encoder_push_debug_group(encoder, &marker)
93 .unwrap(),
94 trace::Command::PopDebugGroup => {
95 self.command_encoder_pop_debug_group(encoder).unwrap()
96 }
97 trace::Command::InsertDebugMarker(marker) => self
98 .command_encoder_insert_debug_marker(encoder, &marker)
99 .unwrap(),
100 trace::Command::RunComputePass {
101 base,
102 timestamp_writes,
103 } => {
104 self.compute_pass_end_with_unresolved_commands(
105 encoder,
106 base,
107 timestamp_writes.as_ref(),
108 );
109 }
110 trace::Command::RunRenderPass {
111 base,
112 target_colors,
113 target_depth_stencil,
114 timestamp_writes,
115 occlusion_query_set_id,
116 } => {
117 self.render_pass_end_with_unresolved_commands(
118 encoder,
119 base,
120 &target_colors,
121 target_depth_stencil.as_ref(),
122 timestamp_writes.as_ref(),
123 occlusion_query_set_id,
124 );
125 }
126 trace::Command::BuildAccelerationStructures { blas, tlas } => {
127 let blas_iter = blas.iter().map(|x| {
128 let geometries = match &x.geometries {
129 wgc::ray_tracing::TraceBlasGeometries::TriangleGeometries(
130 triangle_geometries,
131 ) => {
132 let iter = triangle_geometries.iter().map(|tg| {
133 wgc::ray_tracing::BlasTriangleGeometry {
134 size: &tg.size,
135 vertex_buffer: tg.vertex_buffer,
136 index_buffer: tg.index_buffer,
137 transform_buffer: tg.transform_buffer,
138 first_vertex: tg.first_vertex,
139 vertex_stride: tg.vertex_stride,
140 first_index: tg.first_index,
141 transform_buffer_offset: tg.transform_buffer_offset,
142 }
143 });
144 wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter))
145 }
146 };
147 wgc::ray_tracing::BlasBuildEntry {
148 blas_id: x.blas_id,
149 geometries,
150 }
151 });
152
153 let tlas_iter = tlas.iter().map(|x| {
154 let instances = x.instances.iter().map(|instance| {
155 instance
156 .as_ref()
157 .map(|instance| wgc::ray_tracing::TlasInstance {
158 blas_id: instance.blas_id,
159 transform: &instance.transform,
160 custom_data: instance.custom_data,
161 mask: instance.mask,
162 })
163 });
164 wgc::ray_tracing::TlasPackage {
165 tlas_id: x.tlas_id,
166 instances: Box::new(instances),
167 lowest_unmodified: x.lowest_unmodified,
168 }
169 });
170
171 self.command_encoder_build_acceleration_structures(
172 encoder, blas_iter, tlas_iter,
173 )
174 .unwrap();
175 }
176 }
177 }
178 let (cmd_buf, error) = self.command_encoder_finish(
179 encoder,
180 &wgt::CommandBufferDescriptor { label: None },
181 Some(command_buffer_id_manager.process()),
182 );
183 if let Some(e) = error {
184 panic!("{e}");
185 }
186 cmd_buf
187 }
188
189 fn process(
190 &self,
191 device: wgc::id::DeviceId,
192 queue: wgc::id::QueueId,
193 action: trace::Action,
194 dir: &Path,
195 command_encoder_id_manager: &mut IdentityManager<wgc::id::markers::CommandEncoder>,
196 command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
197 ) {
198 use wgc::device::trace::Action;
199 log::debug!("action {action:?}");
200 match action {
202 Action::Init { .. } => {
203 panic!("Unexpected Action::Init: has to be the first action only")
204 }
205 Action::ConfigureSurface { .. }
206 | Action::Present(_)
207 | Action::DiscardSurfaceTexture(_) => {
208 panic!("Unexpected Surface action: winit feature is not enabled")
209 }
210 Action::CreateBuffer(id, desc) => {
211 let (_, error) = self.device_create_buffer(device, &desc, Some(id));
212 if let Some(e) = error {
213 panic!("{e}");
214 }
215 }
216 Action::FreeBuffer(id) => {
217 self.buffer_destroy(id);
218 }
219 Action::DestroyBuffer(id) => {
220 self.buffer_drop(id);
221 }
222 Action::CreateTexture(id, desc) => {
223 let (_, error) = self.device_create_texture(device, &desc, Some(id));
224 if let Some(e) = error {
225 panic!("{e}");
226 }
227 }
228 Action::FreeTexture(id) => {
229 self.texture_destroy(id);
230 }
231 Action::DestroyTexture(id) => {
232 self.texture_drop(id);
233 }
234 Action::CreateTextureView {
235 id,
236 parent_id,
237 desc,
238 } => {
239 let (_, error) = self.texture_create_view(parent_id, &desc, Some(id));
240 if let Some(e) = error {
241 panic!("{e}");
242 }
243 }
244 Action::DestroyTextureView(id) => {
245 self.texture_view_drop(id).unwrap();
246 }
247 Action::CreateExternalTexture { id, desc, planes } => {
248 let (_, error) =
249 self.device_create_external_texture(device, &desc, &planes, Some(id));
250 if let Some(e) = error {
251 panic!("{e}");
252 }
253 }
254 Action::FreeExternalTexture(id) => {
255 self.external_texture_destroy(id);
256 }
257 Action::DestroyExternalTexture(id) => {
258 self.external_texture_drop(id);
259 }
260 Action::CreateSampler(id, desc) => {
261 let (_, error) = self.device_create_sampler(device, &desc, Some(id));
262 if let Some(e) = error {
263 panic!("{e}");
264 }
265 }
266 Action::DestroySampler(id) => {
267 self.sampler_drop(id);
268 }
269 Action::GetSurfaceTexture { id, parent_id } => {
270 self.surface_get_current_texture(parent_id, Some(id))
271 .unwrap()
272 .texture
273 .unwrap();
274 }
275 Action::CreateBindGroupLayout(id, desc) => {
276 let (_, error) = self.device_create_bind_group_layout(device, &desc, Some(id));
277 if let Some(e) = error {
278 panic!("{e}");
279 }
280 }
281 Action::DestroyBindGroupLayout(id) => {
282 self.bind_group_layout_drop(id);
283 }
284 Action::CreatePipelineLayout(id, desc) => {
285 let (_, error) = self.device_create_pipeline_layout(device, &desc, Some(id));
286 if let Some(e) = error {
287 panic!("{e}");
288 }
289 }
290 Action::DestroyPipelineLayout(id) => {
291 self.pipeline_layout_drop(id);
292 }
293 Action::CreateBindGroup(id, desc) => {
294 let (_, error) = self.device_create_bind_group(device, &desc, Some(id));
295 if let Some(e) = error {
296 panic!("{e}");
297 }
298 }
299 Action::DestroyBindGroup(id) => {
300 self.bind_group_drop(id);
301 }
302 Action::CreateShaderModule { id, desc, data } => {
303 log::debug!("Creating shader from {data}");
304 let code = fs::read_to_string(dir.join(&data)).unwrap();
305 let source = if data.ends_with(".wgsl") {
306 wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code.clone()))
307 } else if data.ends_with(".ron") {
308 let module = ron::de::from_str(&code).unwrap();
309 wgc::pipeline::ShaderModuleSource::Naga(module)
310 } else {
311 panic!("Unknown shader {data}");
312 };
313 let (_, error) = self.device_create_shader_module(device, &desc, source, Some(id));
314 if let Some(e) = error {
315 println!("shader compilation error:\n---{code}\n---\n{e}");
316 }
317 }
318 Action::DestroyShaderModule(id) => {
319 self.shader_module_drop(id);
320 }
321 Action::CreateComputePipeline { id, desc } => {
322 let (_, error) = self.device_create_compute_pipeline(device, &desc, Some(id));
323 if let Some(e) = error {
324 panic!("{e}");
325 }
326 }
327 Action::DestroyComputePipeline(id) => {
328 self.compute_pipeline_drop(id);
329 }
330 Action::CreateRenderPipeline { id, desc } => {
331 let (_, error) = self.device_create_render_pipeline(device, &desc, Some(id));
332 if let Some(e) = error {
333 panic!("{e}");
334 }
335 }
336 Action::DestroyRenderPipeline(id) => {
337 self.render_pipeline_drop(id);
338 }
339 Action::CreatePipelineCache { id, desc } => {
340 let _ = unsafe { self.device_create_pipeline_cache(device, &desc, Some(id)) };
341 }
342 Action::DestroyPipelineCache(id) => {
343 self.pipeline_cache_drop(id);
344 }
345 Action::CreateRenderBundle { id, desc, base } => {
346 let bundle =
347 wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap();
348 let (_, error) = self.render_bundle_encoder_finish(
349 bundle,
350 &wgt::RenderBundleDescriptor { label: desc.label },
351 Some(id),
352 );
353 if let Some(e) = error {
354 panic!("{e}");
355 }
356 }
357 Action::DestroyRenderBundle(id) => {
358 self.render_bundle_drop(id);
359 }
360 Action::CreateQuerySet { id, desc } => {
361 let (_, error) = self.device_create_query_set(device, &desc, Some(id));
362 if let Some(e) = error {
363 panic!("{e}");
364 }
365 }
366 Action::DestroyQuerySet(id) => {
367 self.query_set_drop(id);
368 }
369 Action::WriteBuffer {
370 id,
371 data,
372 range,
373 queued,
374 } => {
375 let bin = std::fs::read(dir.join(data)).unwrap();
376 let size = (range.end - range.start) as usize;
377 if queued {
378 self.queue_write_buffer(queue, id, range.start, &bin)
379 .unwrap();
380 } else {
381 self.device_set_buffer_data(id, range.start, &bin[..size])
382 .unwrap();
383 }
384 }
385 Action::WriteTexture {
386 to,
387 data,
388 layout,
389 size,
390 } => {
391 let bin = std::fs::read(dir.join(data)).unwrap();
392 self.queue_write_texture(queue, &to, &bin, &layout, &size)
393 .unwrap();
394 }
395 Action::Submit(_index, ref commands) if commands.is_empty() => {
396 self.queue_submit(queue, &[]).unwrap();
397 }
398 Action::Submit(_index, commands) => {
399 let (encoder, error) = self.device_create_command_encoder(
400 device,
401 &wgt::CommandEncoderDescriptor { label: None },
402 Some(command_encoder_id_manager.process()),
403 );
404 if let Some(e) = error {
405 panic!("{e}");
406 }
407 let cmdbuf = self.encode_commands(encoder, commands, command_buffer_id_manager);
408 self.queue_submit(queue, &[cmdbuf]).unwrap();
409 }
410 Action::CreateBlas { id, desc, sizes } => {
411 self.device_create_blas(device, &desc, sizes, Some(id));
412 }
413 Action::DestroyBlas(id) => {
414 self.blas_drop(id);
415 }
416 Action::CreateTlas { id, desc } => {
417 self.device_create_tlas(device, &desc, Some(id));
418 }
419 Action::DestroyTlas(id) => {
420 self.tlas_drop(id);
421 }
422 }
423 }
424}