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::{command::Command, 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<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<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 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 Command::CopyBufferToTexture { src, dst, size } => self
52 .command_encoder_copy_buffer_to_texture(encoder, &src, &dst, &size)
53 .unwrap(),
54 Command::CopyTextureToBuffer { src, dst, size } => self
55 .command_encoder_copy_texture_to_buffer(encoder, &src, &dst, &size)
56 .unwrap(),
57 Command::CopyTextureToTexture { src, dst, size } => self
58 .command_encoder_copy_texture_to_texture(encoder, &src, &dst, &size)
59 .unwrap(),
60 Command::ClearBuffer { dst, offset, size } => self
61 .command_encoder_clear_buffer(encoder, dst, offset, size)
62 .unwrap(),
63 Command::ClearTexture {
64 dst,
65 subresource_range,
66 } => self
67 .command_encoder_clear_texture(encoder, dst, &subresource_range)
68 .unwrap(),
69 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 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 Command::PushDebugGroup(marker) => self
92 .command_encoder_push_debug_group(encoder, &marker)
93 .unwrap(),
94 Command::PopDebugGroup => self.command_encoder_pop_debug_group(encoder).unwrap(),
95 Command::InsertDebugMarker(marker) => self
96 .command_encoder_insert_debug_marker(encoder, &marker)
97 .unwrap(),
98 Command::RunComputePass {
99 base,
100 timestamp_writes,
101 } => {
102 self.compute_pass_end_with_unresolved_commands(
103 encoder,
104 base,
105 timestamp_writes.as_ref(),
106 );
107 }
108 Command::RunRenderPass {
109 base,
110 target_colors,
111 target_depth_stencil,
112 timestamp_writes,
113 occlusion_query_set_id,
114 } => {
115 self.render_pass_end_with_unresolved_commands(
116 encoder,
117 base,
118 &target_colors,
119 target_depth_stencil.as_ref(),
120 timestamp_writes.as_ref(),
121 occlusion_query_set_id,
122 );
123 }
124 Command::BuildAccelerationStructures { blas, tlas } => {
125 let blas_iter = blas.iter().map(|x| {
126 let geometries = match &x.geometries {
127 wgc::ray_tracing::TraceBlasGeometries::TriangleGeometries(
128 triangle_geometries,
129 ) => {
130 let iter = triangle_geometries.iter().map(|tg| {
131 wgc::ray_tracing::BlasTriangleGeometry {
132 size: &tg.size,
133 vertex_buffer: tg.vertex_buffer,
134 index_buffer: tg.index_buffer,
135 transform_buffer: tg.transform_buffer,
136 first_vertex: tg.first_vertex,
137 vertex_stride: tg.vertex_stride,
138 first_index: tg.first_index,
139 transform_buffer_offset: tg.transform_buffer_offset,
140 }
141 });
142 wgc::ray_tracing::BlasGeometries::TriangleGeometries(Box::new(iter))
143 }
144 };
145 wgc::ray_tracing::BlasBuildEntry {
146 blas_id: x.blas_id,
147 geometries,
148 }
149 });
150
151 let tlas_iter = tlas.iter().map(|x| {
152 let instances = x.instances.iter().map(|instance| {
153 instance
154 .as_ref()
155 .map(|instance| wgc::ray_tracing::TlasInstance {
156 blas_id: instance.blas_id,
157 transform: &instance.transform,
158 custom_data: instance.custom_data,
159 mask: instance.mask,
160 })
161 });
162 wgc::ray_tracing::TlasPackage {
163 tlas_id: x.tlas_id,
164 instances: Box::new(instances),
165 lowest_unmodified: x.lowest_unmodified,
166 }
167 });
168
169 self.command_encoder_build_acceleration_structures(
170 encoder, blas_iter, tlas_iter,
171 )
172 .unwrap();
173 }
174 }
175 }
176 let (cmd_buf, error) = self.command_encoder_finish(
177 encoder,
178 &wgt::CommandBufferDescriptor { label: None },
179 Some(command_buffer_id_manager.process()),
180 );
181 if let Some(e) = error {
182 panic!("{e}");
183 }
184 cmd_buf
185 }
186
187 fn process(
188 &self,
189 device: wgc::id::DeviceId,
190 queue: wgc::id::QueueId,
191 action: trace::Action,
192 dir: &Path,
193 command_encoder_id_manager: &mut IdentityManager<wgc::id::markers::CommandEncoder>,
194 command_buffer_id_manager: &mut IdentityManager<wgc::id::markers::CommandBuffer>,
195 ) {
196 use wgc::device::trace::Action;
197 log::debug!("action {action:?}");
198 match action {
200 Action::Init { .. } => {
201 panic!("Unexpected Action::Init: has to be the first action only")
202 }
203 Action::ConfigureSurface { .. }
204 | Action::Present(_)
205 | Action::DiscardSurfaceTexture(_) => {
206 panic!("Unexpected Surface action: winit feature is not enabled")
207 }
208 Action::CreateBuffer(id, desc) => {
209 let (_, error) = self.device_create_buffer(device, &desc, Some(id));
210 if let Some(e) = error {
211 panic!("{e}");
212 }
213 }
214 Action::FreeBuffer(id) => {
215 self.buffer_destroy(id);
216 }
217 Action::DestroyBuffer(id) => {
218 self.buffer_drop(id);
219 }
220 Action::CreateTexture(id, desc) => {
221 let (_, error) = self.device_create_texture(device, &desc, Some(id));
222 if let Some(e) = error {
223 panic!("{e}");
224 }
225 }
226 Action::FreeTexture(id) => {
227 self.texture_destroy(id);
228 }
229 Action::DestroyTexture(id) => {
230 self.texture_drop(id);
231 }
232 Action::CreateTextureView {
233 id,
234 parent_id,
235 desc,
236 } => {
237 let (_, error) = self.texture_create_view(parent_id, &desc, Some(id));
238 if let Some(e) = error {
239 panic!("{e}");
240 }
241 }
242 Action::DestroyTextureView(id) => {
243 self.texture_view_drop(id).unwrap();
244 }
245 Action::CreateExternalTexture { id, desc, planes } => {
246 let (_, error) =
247 self.device_create_external_texture(device, &desc, &planes, Some(id));
248 if let Some(e) = error {
249 panic!("{e}");
250 }
251 }
252 Action::FreeExternalTexture(id) => {
253 self.external_texture_destroy(id);
254 }
255 Action::DestroyExternalTexture(id) => {
256 self.external_texture_drop(id);
257 }
258 Action::CreateSampler(id, desc) => {
259 let (_, error) = self.device_create_sampler(device, &desc, Some(id));
260 if let Some(e) = error {
261 panic!("{e}");
262 }
263 }
264 Action::DestroySampler(id) => {
265 self.sampler_drop(id);
266 }
267 Action::GetSurfaceTexture { id, parent_id } => {
268 self.surface_get_current_texture(parent_id, Some(id))
269 .unwrap()
270 .texture
271 .unwrap();
272 }
273 Action::CreateBindGroupLayout(id, desc) => {
274 let (_, error) = self.device_create_bind_group_layout(device, &desc, Some(id));
275 if let Some(e) = error {
276 panic!("{e}");
277 }
278 }
279 Action::DestroyBindGroupLayout(id) => {
280 self.bind_group_layout_drop(id);
281 }
282 Action::CreatePipelineLayout(id, desc) => {
283 let (_, error) = self.device_create_pipeline_layout(device, &desc, Some(id));
284 if let Some(e) = error {
285 panic!("{e}");
286 }
287 }
288 Action::DestroyPipelineLayout(id) => {
289 self.pipeline_layout_drop(id);
290 }
291 Action::CreateBindGroup(id, desc) => {
292 let (_, error) = self.device_create_bind_group(device, &desc, Some(id));
293 if let Some(e) = error {
294 panic!("{e}");
295 }
296 }
297 Action::DestroyBindGroup(id) => {
298 self.bind_group_drop(id);
299 }
300 Action::CreateShaderModule { id, desc, data } => {
301 log::debug!("Creating shader from {data}");
302 let code = fs::read_to_string(dir.join(&data)).unwrap();
303 let source = if data.ends_with(".wgsl") {
304 wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code.clone()))
305 } else if data.ends_with(".ron") {
306 let module = ron::de::from_str(&code).unwrap();
307 wgc::pipeline::ShaderModuleSource::Naga(module)
308 } else {
309 panic!("Unknown shader {data}");
310 };
311 let (_, error) = self.device_create_shader_module(device, &desc, source, Some(id));
312 if let Some(e) = error {
313 println!("shader compilation error:\n---{code}\n---\n{e}");
314 }
315 }
316 Action::CreateShaderModulePassthrough {
317 id,
318 data,
319 entry_point,
320 label,
321 num_workgroups,
322 runtime_checks,
323 } => {
324 let spirv = data.iter().find_map(|a| {
325 if a.ends_with(".spv") {
326 let data = fs::read(dir.join(a)).unwrap();
327 assert!(data.len() % 4 == 0);
328
329 Some(Cow::Owned(bytemuck::pod_collect_to_vec(&data)))
330 } else {
331 None
332 }
333 });
334 let dxil = data.iter().find_map(|a| {
335 if a.ends_with(".dxil") {
336 let vec = std::fs::read(dir.join(a)).unwrap();
337 Some(Cow::Owned(vec))
338 } else {
339 None
340 }
341 });
342 let hlsl = data.iter().find_map(|a| {
343 if a.ends_with(".hlsl") {
344 let code = fs::read_to_string(dir.join(a)).unwrap();
345 Some(Cow::Owned(code))
346 } else {
347 None
348 }
349 });
350 let msl = data.iter().find_map(|a| {
351 if a.ends_with(".msl") {
352 let code = fs::read_to_string(dir.join(a)).unwrap();
353 Some(Cow::Owned(code))
354 } else {
355 None
356 }
357 });
358 let glsl = data.iter().find_map(|a| {
359 if a.ends_with(".glsl") {
360 let code = fs::read_to_string(dir.join(a)).unwrap();
361 Some(Cow::Owned(code))
362 } else {
363 None
364 }
365 });
366 let wgsl = data.iter().find_map(|a| {
367 if a.ends_with(".wgsl") {
368 let code = fs::read_to_string(dir.join(a)).unwrap();
369 Some(Cow::Owned(code))
370 } else {
371 None
372 }
373 });
374 let desc = wgt::CreateShaderModuleDescriptorPassthrough {
375 entry_point,
376 label,
377 num_workgroups,
378 runtime_checks,
379
380 spirv,
381 dxil,
382 hlsl,
383 msl,
384 glsl,
385 wgsl,
386 };
387 let (_, error) = unsafe {
388 self.device_create_shader_module_passthrough(device, &desc, Some(id))
389 };
390 if let Some(e) = error {
391 println!("shader compilation error: {e}");
392 }
393 }
394 Action::DestroyShaderModule(id) => {
395 self.shader_module_drop(id);
396 }
397 Action::CreateComputePipeline { id, desc } => {
398 let (_, error) = self.device_create_compute_pipeline(device, &desc, Some(id));
399 if let Some(e) = error {
400 panic!("{e}");
401 }
402 }
403 Action::DestroyComputePipeline(id) => {
404 self.compute_pipeline_drop(id);
405 }
406 Action::CreateRenderPipeline { id, desc } => {
407 let (_, error) = self.device_create_render_pipeline(device, &desc, Some(id));
408 if let Some(e) = error {
409 panic!("{e}");
410 }
411 }
412 Action::CreateMeshPipeline { id, desc } => {
413 let (_, error) = self.device_create_mesh_pipeline(device, &desc, Some(id));
414 if let Some(e) = error {
415 panic!("{e}");
416 }
417 }
418 Action::DestroyRenderPipeline(id) => {
419 self.render_pipeline_drop(id);
420 }
421 Action::CreatePipelineCache { id, desc } => {
422 let _ = unsafe { self.device_create_pipeline_cache(device, &desc, Some(id)) };
423 }
424 Action::DestroyPipelineCache(id) => {
425 self.pipeline_cache_drop(id);
426 }
427 Action::CreateRenderBundle { id, desc, base } => {
428 let bundle =
429 wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap();
430 let (_, error) = self.render_bundle_encoder_finish(
431 bundle,
432 &wgt::RenderBundleDescriptor { label: desc.label },
433 Some(id),
434 );
435 if let Some(e) = error {
436 panic!("{e}");
437 }
438 }
439 Action::DestroyRenderBundle(id) => {
440 self.render_bundle_drop(id);
441 }
442 Action::CreateQuerySet { id, desc } => {
443 let (_, error) = self.device_create_query_set(device, &desc, Some(id));
444 if let Some(e) = error {
445 panic!("{e}");
446 }
447 }
448 Action::DestroyQuerySet(id) => {
449 self.query_set_drop(id);
450 }
451 Action::WriteBuffer {
452 id,
453 data,
454 range,
455 queued,
456 } => {
457 let bin = std::fs::read(dir.join(data)).unwrap();
458 let size = (range.end - range.start) as usize;
459 if queued {
460 self.queue_write_buffer(queue, id, range.start, &bin)
461 .unwrap();
462 } else {
463 self.device_set_buffer_data(id, range.start, &bin[..size])
464 .unwrap();
465 }
466 }
467 Action::WriteTexture {
468 to,
469 data,
470 layout,
471 size,
472 } => {
473 let bin = std::fs::read(dir.join(data)).unwrap();
474 self.queue_write_texture(queue, &to, &bin, &layout, &size)
475 .unwrap();
476 }
477 Action::Submit(_index, ref commands) if commands.is_empty() => {
478 self.queue_submit(queue, &[]).unwrap();
479 }
480 Action::Submit(_index, commands) => {
481 let (encoder, error) = self.device_create_command_encoder(
482 device,
483 &wgt::CommandEncoderDescriptor { label: None },
484 Some(command_encoder_id_manager.process()),
485 );
486 if let Some(e) = error {
487 panic!("{e}");
488 }
489 let cmdbuf = self.encode_commands(encoder, commands, command_buffer_id_manager);
490 self.queue_submit(queue, &[cmdbuf]).unwrap();
491 }
492 Action::CreateBlas { id, desc, sizes } => {
493 self.device_create_blas(device, &desc, sizes, Some(id));
494 }
495 Action::DestroyBlas(id) => {
496 self.blas_drop(id);
497 }
498 Action::CreateTlas { id, desc } => {
499 self.device_create_tlas(device, &desc, Some(id));
500 }
501 Action::DestroyTlas(id) => {
502 self.tlas_drop(id);
503 }
504 }
505 }
506}