1use std::pin::Pin;
4use std::sync::Arc;
5
6use wasdk::webgpu::{self, Gpu, GpuRequestAdapterOptions};
7use wgpu::custom::{
8 DispatchAdapter, DispatchSurface, EnumerateAdapterFuture, InstanceInterface,
9 RequestAdapterFuture,
10};
11use wgpu::{Backends, RequestAdapterError, WgslLanguageFeatures};
12
13use crate::adapter::WasdkAdapter;
14use crate::convert;
15use crate::gap::wasi_gap;
16use crate::surface::{SharedView, WasdkSurface, shared_view_of};
17
18#[derive(Debug)]
21pub struct WasdkInstance {
22 gpu: Arc<Gpu>,
23 requested_backends: Backends,
25}
26
27impl WasdkInstance {
28 pub fn gpu(&self) -> &Gpu {
30 &self.gpu
31 }
32
33 pub(crate) fn preferred_canvas_format(&self) -> wgpu::TextureFormat {
35 convert::texture_format_from_wasi(self.gpu.get_preferred_canvas_format())
36 }
37}
38
39const BACKEND: Backends = Backends::BROWSER_WEBGPU;
41
42const WGSL_LANGUAGE_FEATURES: &[(WgslLanguageFeatures, &str)] = &[
43 (
44 WgslLanguageFeatures::ReadOnlyAndReadWriteStorageTextures,
45 "readonly_and_readwrite_storage_textures",
46 ),
47 (
48 WgslLanguageFeatures::Packed4x8IntegerDotProduct,
49 "packed_4x8_integer_dot_product",
50 ),
51 (
52 WgslLanguageFeatures::UnrestrictedPointerParameters,
53 "unrestricted_pointer_parameters",
54 ),
55 (
56 WgslLanguageFeatures::PointerCompositeAccess,
57 "pointer_composite_access",
58 ),
59];
60
61#[allow(unsafe_code)]
63impl InstanceInterface for WasdkInstance {
64 fn new(desc: wgpu::InstanceDescriptor) -> Self {
65 WasdkInstance {
66 gpu: Arc::new(webgpu::get_gpu()),
67 requested_backends: desc.backends,
68 }
69 }
70
71 unsafe fn create_surface(
74 &self,
75 target: wgpu::SurfaceTargetUnsafe,
76 ) -> Result<DispatchSurface, wgpu::CreateSurfaceError> {
77 let wgpu::SurfaceTargetUnsafe::RawHandle {
78 raw_window_handle, ..
79 } = target
80 else {
81 wasi_gap("a surface from a target other than a window handle")
82 };
83 let pointer = shared_view_of(raw_window_handle);
84 let view: &SharedView = unsafe { pointer.as_ref() };
87 let surface = WasdkSurface::new(view, self.preferred_canvas_format());
88 Ok(DispatchSurface::custom(surface))
89 }
90
91 fn request_adapter(
92 &self,
93 options: &wgpu::RequestAdapterOptions<'_, '_>,
94 ) -> Pin<Box<dyn RequestAdapterFuture>> {
95 let requested_backends = self.requested_backends;
97 let wasi_options = requested_backends
98 .contains(BACKEND)
99 .then(|| GpuRequestAdapterOptions {
100 feature_level: None,
101 power_preference: convert::power_preference_to_wasi(options.power_preference),
102 force_fallback_adapter: Some(options.force_fallback_adapter),
103 xr_compatible: None,
104 });
105 let gpu = Arc::clone(&self.gpu);
106 Box::pin(async move {
107 let adapter = match wasi_options {
108 Some(wasi_options) => gpu.request_adapter(Some(wasi_options)).await,
109 None => None,
110 };
111 adapter
112 .map(|adapter| DispatchAdapter::custom(WasdkAdapter::new(adapter)))
113 .ok_or_else(|| no_adapter(requested_backends))
114 })
115 }
116
117 fn poll_all_devices(&self, _force_wait: bool) -> bool {
118 true
120 }
121
122 fn wgsl_language_features(&self) -> WgslLanguageFeatures {
123 let supported = self.gpu.wgsl_language_features();
124 WGSL_LANGUAGE_FEATURES
125 .iter()
126 .filter(|(_, name)| supported.has(name))
127 .fold(WgslLanguageFeatures::empty(), |features, (bit, _)| {
128 features | bit.clone()
129 })
130 }
131
132 fn enumerate_adapters(&self, backends: Backends) -> Pin<Box<dyn EnumerateAdapterFuture>> {
133 if !backends.contains(BACKEND) {
134 return Box::pin(async { Vec::new() });
135 }
136 let adapter = self.request_adapter(&wgpu::RequestAdapterOptions::default());
137 Box::pin(async move { adapter.await.into_iter().collect() })
138 }
139}
140
141fn no_adapter(requested_backends: Backends) -> RequestAdapterError {
142 RequestAdapterError::NotFound {
143 active_backends: BACKEND,
144 requested_backends,
145 supported_backends: BACKEND,
146 no_fallback_backends: Backends::empty(),
147 no_adapter_backends: BACKEND,
148 incompatible_surface_backends: Backends::empty(),
149 }
150}