1use std::pin::Pin;
4use std::sync::Arc;
5use std::sync::atomic::{AtomicU32, Ordering};
6
7use futures::StreamExt;
8use wasdk::webgpu::GpuDevice;
9use wgpu::custom::{
10 BoxDeviceLostCallback, DeviceInterface, DispatchBindGroup, DispatchBindGroupLayout,
11 DispatchBlas, DispatchBuffer, DispatchCommandEncoder, DispatchComputePipeline, DispatchDevice,
12 DispatchExternalTexture, DispatchPipelineCache, DispatchPipelineLayout, DispatchQuerySet,
13 DispatchRenderBundleEncoder, DispatchRenderPipeline, DispatchSampler, DispatchShaderModule,
14 DispatchTexture, DispatchTlas, PopErrorScopeFuture,
15};
16use wgpu::{AdapterInfo, Features, Limits};
17
18use crate::gap::wasi_gap;
19use crate::{
20 binding, buffer, command, convert, pass, pipeline, query, runtime, sampler, shader, texture,
21};
22
23#[derive(Debug)]
25pub struct WasdkDevice {
26 device: Arc<GpuDevice>,
27 open_error_scopes: AtomicU32,
29}
30
31downcast!(WasdkDevice: api wgpu::Device, dispatch DispatchDevice);
32
33impl WasdkDevice {
34 pub(crate) fn new(device: GpuDevice) -> Self {
35 WasdkDevice {
36 device: Arc::new(device),
37 open_error_scopes: AtomicU32::new(0),
38 }
39 }
40
41 pub fn gpu_device(&self) -> &GpuDevice {
44 &self.device
45 }
46}
47
48#[allow(unsafe_code)]
51impl DeviceInterface for WasdkDevice {
52 fn features(&self) -> Features {
53 convert::features_from_wasi(&self.device.features())
54 }
55
56 fn limits(&self) -> Limits {
57 convert::limits_from_wasi(&self.device.limits())
58 }
59
60 fn adapter_info(&self) -> AdapterInfo {
61 convert::adapter_info_from_wasi(&self.device.adapter_info())
62 }
63
64 fn create_shader_module(
65 &self,
66 desc: wgpu::ShaderModuleDescriptor<'_>,
67 _runtime_checks: wgpu::ShaderRuntimeChecks,
68 ) -> DispatchShaderModule {
69 shader::create_shader_module(&self.device, &desc)
70 }
71
72 unsafe fn create_shader_module_passthrough(
73 &self,
74 desc: &wgpu::ShaderModuleDescriptorPassthrough<'_>,
75 ) -> DispatchShaderModule {
76 shader::create_shader_module_passthrough(&self.device, desc)
77 }
78
79 fn create_bind_group_layout(
80 &self,
81 desc: &wgpu::BindGroupLayoutDescriptor<'_>,
82 ) -> DispatchBindGroupLayout {
83 binding::create_bind_group_layout(&self.device, desc)
84 }
85
86 fn create_bind_group(&self, desc: &wgpu::BindGroupDescriptor<'_>) -> DispatchBindGroup {
87 binding::create_bind_group(&self.device, desc)
88 }
89
90 fn create_pipeline_layout(
91 &self,
92 desc: &wgpu::PipelineLayoutDescriptor<'_>,
93 ) -> DispatchPipelineLayout {
94 pipeline::create_pipeline_layout(&self.device, desc)
95 }
96
97 fn create_render_pipeline(
98 &self,
99 desc: &wgpu::RenderPipelineDescriptor<'_>,
100 ) -> DispatchRenderPipeline {
101 pipeline::create_render_pipeline(&self.device, desc)
102 }
103
104 fn create_mesh_pipeline(
105 &self,
106 _desc: &wgpu::MeshPipelineDescriptor<'_>,
107 ) -> DispatchRenderPipeline {
108 wasi_gap("mesh shaders (Device::create_mesh_pipeline)")
109 }
110
111 fn create_compute_pipeline(
112 &self,
113 desc: &wgpu::ComputePipelineDescriptor<'_>,
114 ) -> DispatchComputePipeline {
115 pipeline::create_compute_pipeline(&self.device, desc)
116 }
117
118 unsafe fn create_pipeline_cache(
119 &self,
120 _desc: &wgpu::PipelineCacheDescriptor<'_>,
121 ) -> DispatchPipelineCache {
122 wasi_gap("pipeline caches (Device::create_pipeline_cache)")
123 }
124
125 fn create_buffer(&self, desc: &wgpu::BufferDescriptor<'_>) -> DispatchBuffer {
126 buffer::create_buffer(&self.device, desc)
127 }
128
129 fn create_texture(&self, desc: &wgpu::TextureDescriptor<'_>) -> DispatchTexture {
130 texture::create_texture(&self.device, desc)
131 }
132
133 fn create_external_texture(
134 &self,
135 _desc: &wgpu::ExternalTextureDescriptor<'_>,
136 _planes: &[&wgpu::TextureView],
137 ) -> DispatchExternalTexture {
138 wasi_gap("external textures (Device::create_external_texture)")
139 }
140
141 fn create_blas(
142 &self,
143 _desc: &wgpu::CreateBlasDescriptor<'_>,
144 _sizes: wgpu::BlasGeometrySizeDescriptors,
145 ) -> (Option<u64>, DispatchBlas) {
146 wasi_gap("ray tracing (Device::create_blas)")
147 }
148
149 fn create_tlas(&self, _desc: &wgpu::CreateTlasDescriptor<'_>) -> DispatchTlas {
150 wasi_gap("ray tracing (Device::create_tlas)")
151 }
152
153 fn create_sampler(&self, desc: &wgpu::SamplerDescriptor<'_>) -> DispatchSampler {
154 sampler::create_sampler(&self.device, desc)
155 }
156
157 fn create_query_set(&self, desc: &wgpu::QuerySetDescriptor<'_>) -> DispatchQuerySet {
158 query::create_query_set(&self.device, desc)
159 }
160
161 fn create_command_encoder(
162 &self,
163 desc: &wgpu::CommandEncoderDescriptor<'_>,
164 ) -> DispatchCommandEncoder {
165 command::create_command_encoder(&self.device, desc)
166 }
167
168 fn create_render_bundle_encoder(
169 &self,
170 desc: &wgpu::RenderBundleEncoderDescriptor<'_>,
171 ) -> DispatchRenderBundleEncoder {
172 pass::create_render_bundle_encoder(&self.device, desc)
173 }
174
175 fn set_device_lost_callback(&self, callback: BoxDeviceLostCallback) {
176 let lost = self.device.lost();
177 runtime::spawn(async move {
178 let info = lost.await;
179 callback(
180 convert::device_lost_reason_from_wasi(info.reason()),
181 info.message(),
182 );
183 });
184 }
185
186 fn on_uncaptured_error(&self, handler: Arc<dyn wgpu::UncapturedErrorHandler>) {
187 let mut errors = self.device.on_uncaptured_error().into_stream();
188 runtime::spawn(async move {
189 while let Some(error) = errors.next().await {
190 handler(convert::error_from_wasi(&error));
191 }
192 });
193 }
194
195 fn push_error_scope(&self, filter: wgpu::ErrorFilter) -> u32 {
196 let index = self.open_error_scopes.fetch_add(1, Ordering::Relaxed);
197 self.device
198 .push_error_scope(convert::error_filter_to_wasi(filter));
199 index
200 }
201
202 fn pop_error_scope(&self, index: u32) -> Pin<Box<dyn PopErrorScopeFuture>> {
203 let open = self.open_error_scopes.load(Ordering::Relaxed);
204 assert!(
205 open == index + 1,
206 "wasdk-wgpu: error scope {index} popped with {open} open; scopes pop in reverse order"
207 );
208 self.open_error_scopes.store(index, Ordering::Relaxed);
209 let device = Arc::clone(&self.device);
210 Box::pin(async move {
211 let error = device.pop_error_scope().await.ok().flatten()?;
213 Some(convert::error_from_wasi(&error))
214 })
215 }
216
217 unsafe fn start_graphics_debugger_capture(&self) {
218 }
220
221 unsafe fn stop_graphics_debugger_capture(&self) {
222 }
224
225 fn poll(
226 &self,
227 _poll_type: wgpu::wgt::PollType<u64>,
228 ) -> Result<wgpu::PollStatus, wgpu::PollError> {
229 Ok(wgpu::PollStatus::QueueEmpty)
231 }
232
233 fn get_internal_counters(&self) -> wgpu::InternalCounters {
234 wgpu::InternalCounters::default()
235 }
236
237 fn generate_allocator_report(&self) -> Option<wgpu::AllocatorReport> {
238 None
239 }
240
241 fn destroy(&self) {
242 self.device.destroy();
243 }
244}