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::CreateShaderModulePassthrough {
319 id,
320 data,
321 entry_point,
322 label,
323 num_workgroups,
324 runtime_checks,
325 } => {
326 let spirv = data.iter().find_map(|a| {
327 if a.ends_with(".spv") {
328 let data = fs::read(dir.join(a)).unwrap();
329 assert!(data.len() % 4 == 0);
330
331 Some(Cow::Owned(bytemuck::pod_collect_to_vec(&data)))
332 } else {
333 None
334 }
335 });
336 let dxil = data.iter().find_map(|a| {
337 if a.ends_with(".dxil") {
338 let vec = std::fs::read(dir.join(a)).unwrap();
339 Some(Cow::Owned(vec))
340 } else {
341 None
342 }
343 });
344 let hlsl = data.iter().find_map(|a| {
345 if a.ends_with(".hlsl") {
346 let code = fs::read_to_string(dir.join(a)).unwrap();
347 Some(Cow::Owned(code))
348 } else {
349 None
350 }
351 });
352 let msl = data.iter().find_map(|a| {
353 if a.ends_with(".msl") {
354 let code = fs::read_to_string(dir.join(a)).unwrap();
355 Some(Cow::Owned(code))
356 } else {
357 None
358 }
359 });
360 let glsl = data.iter().find_map(|a| {
361 if a.ends_with(".glsl") {
362 let code = fs::read_to_string(dir.join(a)).unwrap();
363 Some(Cow::Owned(code))
364 } else {
365 None
366 }
367 });
368 let wgsl = data.iter().find_map(|a| {
369 if a.ends_with(".wgsl") {
370 let code = fs::read_to_string(dir.join(a)).unwrap();
371 Some(Cow::Owned(code))
372 } else {
373 None
374 }
375 });
376 let desc = wgt::CreateShaderModuleDescriptorPassthrough {
377 entry_point,
378 label,
379 num_workgroups,
380 runtime_checks,
381
382 spirv,
383 dxil,
384 hlsl,
385 msl,
386 glsl,
387 wgsl,
388 };
389 let (_, error) = unsafe {
390 self.device_create_shader_module_passthrough(device, &desc, Some(id))
391 };
392 if let Some(e) = error {
393 println!("shader compilation error: {e}");
394 }
395 }
396 Action::DestroyShaderModule(id) => {
397 self.shader_module_drop(id);
398 }
399 Action::CreateComputePipeline { id, desc } => {
400 let (_, error) = self.device_create_compute_pipeline(device, &desc, Some(id));
401 if let Some(e) = error {
402 panic!("{e}");
403 }
404 }
405 Action::DestroyComputePipeline(id) => {
406 self.compute_pipeline_drop(id);
407 }
408 Action::CreateRenderPipeline { id, desc } => {
409 let (_, error) = self.device_create_render_pipeline(device, &desc, Some(id));
410 if let Some(e) = error {
411 panic!("{e}");
412 }
413 }
414 Action::CreateMeshPipeline { id, desc } => {
415 let (_, error) = self.device_create_mesh_pipeline(device, &desc, Some(id));
416 if let Some(e) = error {
417 panic!("{e}");
418 }
419 }
420 Action::DestroyRenderPipeline(id) => {
421 self.render_pipeline_drop(id);
422 }
423 Action::CreatePipelineCache { id, desc } => {
424 let _ = unsafe { self.device_create_pipeline_cache(device, &desc, Some(id)) };
425 }
426 Action::DestroyPipelineCache(id) => {
427 self.pipeline_cache_drop(id);
428 }
429 Action::CreateRenderBundle { id, desc, base } => {
430 let bundle =
431 wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap();
432 let (_, error) = self.render_bundle_encoder_finish(
433 bundle,
434 &wgt::RenderBundleDescriptor { label: desc.label },
435 Some(id),
436 );
437 if let Some(e) = error {
438 panic!("{e}");
439 }
440 }
441 Action::DestroyRenderBundle(id) => {
442 self.render_bundle_drop(id);
443 }
444 Action::CreateQuerySet { id, desc } => {
445 let (_, error) = self.device_create_query_set(device, &desc, Some(id));
446 if let Some(e) = error {
447 panic!("{e}");
448 }
449 }
450 Action::DestroyQuerySet(id) => {
451 self.query_set_drop(id);
452 }
453 Action::WriteBuffer {
454 id,
455 data,
456 range,
457 queued,
458 } => {
459 let bin = std::fs::read(dir.join(data)).unwrap();
460 let size = (range.end - range.start) as usize;
461 if queued {
462 self.queue_write_buffer(queue, id, range.start, &bin)
463 .unwrap();
464 } else {
465 self.device_set_buffer_data(id, range.start, &bin[..size])
466 .unwrap();
467 }
468 }
469 Action::WriteTexture {
470 to,
471 data,
472 layout,
473 size,
474 } => {
475 let bin = std::fs::read(dir.join(data)).unwrap();
476 self.queue_write_texture(queue, &to, &bin, &layout, &size)
477 .unwrap();
478 }
479 Action::Submit(_index, ref commands) if commands.is_empty() => {
480 self.queue_submit(queue, &[]).unwrap();
481 }
482 Action::Submit(_index, commands) => {
483 let (encoder, error) = self.device_create_command_encoder(
484 device,
485 &wgt::CommandEncoderDescriptor { label: None },
486 Some(command_encoder_id_manager.process()),
487 );
488 if let Some(e) = error {
489 panic!("{e}");
490 }
491 let cmdbuf = self.encode_commands(encoder, commands, command_buffer_id_manager);
492 self.queue_submit(queue, &[cmdbuf]).unwrap();
493 }
494 Action::CreateBlas { id, desc, sizes } => {
495 self.device_create_blas(device, &desc, sizes, Some(id));
496 }
497 Action::DestroyBlas(id) => {
498 self.blas_drop(id);
499 }
500 Action::CreateTlas { id, desc } => {
501 self.device_create_tlas(device, &desc, Some(id));
502 }
503 Action::DestroyTlas(id) => {
504 self.tlas_drop(id);
505 }
506 }
507 }
508}