Skip to content

Commit c0ff4bf

Browse files
jimblandyclaude
andcommitted
Implement transparent pass-throughs for the remaining audit-layer stubs
Turns the ~113 todo!() stubs across Device, Queue, CommandEncoder, and the rest of Adapter/Surface into calls that forward to `inner`, allocating a fresh audit `Id` for each newly created resource. No new safety checking is added here — this just makes the wrapper functionally complete (usable end to end) instead of panicking on first use of anything beyond instance creation and adapter enumeration. Since `Audited<T>::inner` is already stored as the erased `Box<dyn DynX>` the real backend expects, converting an audited-wrapper descriptor into its `dyn` equivalent needs no downcasting — just `&self .inner`. Added two small helpers to make that idiomatic: `Audited::wrap` (allocate an id, wrap a freshly created inner resource) and `Audited::as_dyn` (deref `inner` to its erased type). Descriptors that are themselves generic over other resource types (BindGroupDescriptor, RenderPipelineDescriptor, RenderPassDescriptor, BuildAccelerationStructureDescriptor, etc.) get reconstructed field-by-field against their `dyn` equivalents, mirroring the existing reverse-direction conversions in wgpu-hal/src/dynamic/*.rs (which do the same job the other way, backend -> dyn, complete with real downcasts since they don't have the luxury of already holding the erased type). Two methods are left as todo!(), each with a comment explaining why: - `Surface::acquire_texture` needs to cache an owned `audit::Texture` so `Borrow<audit::Texture>` has something to point to, but the only texture available is a *borrowed* view of the inner surface texture. Manufacturing an owned `Box<dyn DynTexture>` from that borrow would need either downcasting to a concrete backend type (this layer is backend-agnostic) or aliasing `inner`'s allocation (double-free once both boxes drop). Needs an actual design change, not a mechanical fix. - `CommandEncoder::set_acceleration_structure_dependencies` is declared as a receiverless associated function in the static `CommandEncoder` trait, so there is no `self.inner` to forward to. Added wgpu-hal/examples/audit_smoke.rs, a standalone example that drives a real Vulkan instance through the auditing wrapper end to end (create instance, enumerate adapters, open a device, write to a mapped buffer, record+submit+wait on a command buffer) to exercise this. Verified with `WGPU_AUDIT_HAL_USAGE=1 cargo run -p wgpu-hal --example audit_smoke --features vulkan`, comparing against the same run with the env var unset. `cargo check --workspace --features vulkan` and `cargo clippy -p wgpu-hal --features vulkan --all-targets` are both clean. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent abdb954 commit c0ff4bf

8 files changed

Lines changed: 737 additions & 123 deletions

File tree

wgpu-hal/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ name = "halmark"
202202
name = "raw-gles"
203203
required-features = ["gles"]
204204

205+
[[example]]
206+
name = "audit_smoke"
207+
required-features = ["vulkan"]
208+
205209
#####################
206210
### Platform: All ###
207211
#####################

wgpu-hal/examples/audit_smoke.rs

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//! A smoke test for `wgpu_hal::audit`.
2+
//!
3+
//! This drives a real Vulkan instance through the auditing wrapper end to
4+
//! end: enumerate adapters, open a device, create a buffer and write to it
5+
//! via mapping, then record and submit an (empty) command buffer and wait
6+
//! for it to finish. Every call in this program passes through
7+
//! `wgpu_hal::audit`'s `Audited` wrappers before reaching the real Vulkan
8+
//! backend.
9+
//!
10+
//! Run with `WGPU_AUDIT_HAL_USAGE` unset to compare against the
11+
//! unaudited path; the two should behave identically.
12+
13+
use wgpu_hal::Instance as _;
14+
15+
fn main() {
16+
env_logger::init();
17+
unsafe { run() };
18+
}
19+
20+
unsafe fn run() {
21+
let instance_desc = wgpu_hal::InstanceDescriptor {
22+
name: "audit-smoke",
23+
flags: wgpu_types::InstanceFlags::debugging(),
24+
memory_budget_thresholds: Default::default(),
25+
backend_options: Default::default(),
26+
telemetry: None,
27+
display: None,
28+
};
29+
30+
let instance: Box<dyn wgpu_hal::DynInstance> = Box::new(
31+
unsafe { <wgpu_hal::vulkan::Api as wgpu_hal::Api>::Instance::init(&instance_desc) }
32+
.expect("failed to create a vulkan instance"),
33+
);
34+
35+
let audit_enabled = std::env::var("WGPU_AUDIT_HAL_USAGE").is_ok_and(|v| v != "0");
36+
let instance = if audit_enabled {
37+
println!("WGPU_AUDIT_HAL_USAGE set: wrapping the instance in the auditing layer");
38+
wgpu_hal::audit::new_auditing_instance(
39+
instance,
40+
wgpu_types::Backend::Vulkan,
41+
wgpu_hal::audit::report_by_log(log::Level::Info),
42+
)
43+
} else {
44+
println!("WGPU_AUDIT_HAL_USAGE not set: using the instance unaudited");
45+
instance
46+
};
47+
48+
let exposed = unsafe { instance.enumerate_adapters(None) }
49+
.into_iter()
50+
.next()
51+
.expect("no adapters found");
52+
println!("Using adapter: {}", exposed.info.name);
53+
54+
let open_device = unsafe {
55+
exposed.adapter.open(
56+
exposed.features,
57+
&wgpu_types::Limits::default(),
58+
&wgpu_types::MemoryHints::default(),
59+
)
60+
}
61+
.expect("failed to open device");
62+
let device = open_device.device;
63+
let queue = open_device.queue;
64+
65+
const SIZE: wgpu_types::BufferAddress = 256;
66+
let buffer = unsafe {
67+
device.create_buffer(&wgpu_hal::BufferDescriptor {
68+
label: Some("audit-smoke buffer"),
69+
size: SIZE,
70+
usage: wgpu_types::BufferUses::MAP_WRITE
71+
| wgpu_types::BufferUses::MAP_READ
72+
| wgpu_types::BufferUses::COPY_SRC
73+
| wgpu_types::BufferUses::COPY_DST,
74+
memory_flags: wgpu_hal::MemoryFlags::empty(),
75+
})
76+
}
77+
.expect("failed to create buffer");
78+
79+
unsafe {
80+
let mapping = device
81+
.map_buffer(&*buffer, 0..SIZE)
82+
.expect("failed to map buffer");
83+
core::ptr::write_bytes(mapping.ptr.as_ptr(), 0x42, SIZE as usize);
84+
#[allow(clippy::single_range_in_vec_init)]
85+
device.flush_mapped_ranges(&*buffer, &[0..SIZE]);
86+
device.unmap_buffer(&*buffer);
87+
}
88+
println!("Wrote {SIZE} bytes to a mapped buffer");
89+
90+
let mut encoder = unsafe {
91+
device.create_command_encoder(&wgpu_hal::CommandEncoderDescriptor {
92+
label: Some("audit-smoke encoder"),
93+
queue: &*queue,
94+
})
95+
}
96+
.expect("failed to create command encoder");
97+
98+
let command_buffer = unsafe {
99+
encoder
100+
.begin_encoding(Some("audit-smoke pass"))
101+
.expect("failed to begin encoding");
102+
encoder.end_encoding().expect("failed to end encoding")
103+
};
104+
105+
let fence = unsafe { device.create_fence() }.expect("failed to create fence");
106+
unsafe {
107+
queue
108+
.submit(&[&*command_buffer], &[], (&*fence, 1))
109+
.expect("submit failed");
110+
device.wait(&*fence, 1, None).expect("wait failed");
111+
}
112+
println!("Submitted and waited on an empty command buffer");
113+
114+
unsafe {
115+
encoder.reset_all(vec![command_buffer]);
116+
device.destroy_fence(fence);
117+
device.destroy_buffer(buffer);
118+
}
119+
120+
println!("audit smoke test completed successfully");
121+
}

wgpu-hal/src/audit/adapter.rs

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -12,45 +12,35 @@ impl crate::Adapter for super::Adapter {
1212
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
1313
let crate::DynOpenDevice { device, queue } =
1414
unsafe { self.inner.open(features, limits, memory_hints)? };
15-
let device_id = self.shared.new_id();
16-
let queue_id = self.shared.new_id();
1715
Ok(crate::OpenDevice {
18-
device: super::Device {
19-
inner: device,
20-
id: device_id,
21-
shared: self.shared.clone(),
22-
},
23-
queue: super::Queue {
24-
inner: queue,
25-
id: queue_id,
26-
shared: self.shared.clone(),
27-
},
16+
device: super::Device::wrap(device, self.shared.clone()),
17+
queue: super::Queue::wrap(queue, self.shared.clone()),
2818
})
2919
}
3020

3121
unsafe fn texture_format_capabilities(
3222
&self,
3323
format: wgt::TextureFormat,
3424
) -> crate::TextureFormatCapabilities {
35-
todo!()
25+
unsafe { self.inner.texture_format_capabilities(format) }
3626
}
3727

3828
unsafe fn surface_capabilities(
3929
&self,
4030
surface: &<super::Api as crate::Api>::Surface,
4131
) -> Option<crate::SurfaceCapabilities> {
42-
todo!()
32+
unsafe { self.inner.surface_capabilities(surface.as_dyn()) }
4333
}
4434

4535
unsafe fn get_presentation_timestamp(&self) -> wgt::PresentationTimestamp {
46-
todo!()
36+
unsafe { self.inner.get_presentation_timestamp() }
4737
}
4838

4939
fn get_ordered_buffer_usages(&self) -> wgt::BufferUses {
50-
todo!()
40+
self.inner.get_ordered_buffer_usages()
5141
}
5242

5343
fn get_ordered_texture_usages(&self) -> wgt::TextureUses {
54-
todo!()
44+
self.inner.get_ordered_texture_usages()
5545
}
5646
}

0 commit comments

Comments
 (0)