use std::pin::Pin;
use std::sync::Arc;
use wasdk::webgpu::{self, Gpu, GpuRequestAdapterOptions};
use wgpu::custom::{
DispatchAdapter, DispatchSurface, EnumerateAdapterFuture, InstanceInterface,
RequestAdapterFuture,
};
use wgpu::{Backends, RequestAdapterError, WgslLanguageFeatures};
use crate::adapter::WasdkAdapter;
use crate::convert;
use crate::gap::wasi_gap;
use crate::surface::{SharedView, WasdkSurface, shared_view_of};
#[derive(Debug)]
pub struct WasdkInstance {
gpu: Arc<Gpu>,
requested_backends: Backends,
}
impl WasdkInstance {
pub fn gpu(&self) -> &Gpu {
&self.gpu
}
pub(crate) fn preferred_canvas_format(&self) -> wgpu::TextureFormat {
convert::texture_format_from_wasi(self.gpu.get_preferred_canvas_format())
}
}
const BACKEND: Backends = Backends::BROWSER_WEBGPU;
const WGSL_LANGUAGE_FEATURES: &[(WgslLanguageFeatures, &str)] = &[
(
WgslLanguageFeatures::ReadOnlyAndReadWriteStorageTextures,
"readonly_and_readwrite_storage_textures",
),
(
WgslLanguageFeatures::Packed4x8IntegerDotProduct,
"packed_4x8_integer_dot_product",
),
(
WgslLanguageFeatures::UnrestrictedPointerParameters,
"unrestricted_pointer_parameters",
),
(
WgslLanguageFeatures::PointerCompositeAccess,
"pointer_composite_access",
),
];
#[allow(unsafe_code)]
impl InstanceInterface for WasdkInstance {
fn new(desc: wgpu::InstanceDescriptor) -> Self {
WasdkInstance {
gpu: Arc::new(webgpu::get_gpu()),
requested_backends: desc.backends,
}
}
unsafe fn create_surface(
&self,
target: wgpu::SurfaceTargetUnsafe,
) -> Result<DispatchSurface, wgpu::CreateSurfaceError> {
let wgpu::SurfaceTargetUnsafe::RawHandle {
raw_window_handle, ..
} = target
else {
wasi_gap("a surface from a target other than a window handle")
};
let pointer = shared_view_of(raw_window_handle);
let view: &SharedView = unsafe { pointer.as_ref() };
let surface = WasdkSurface::new(view, self.preferred_canvas_format());
Ok(DispatchSurface::custom(surface))
}
fn request_adapter(
&self,
options: &wgpu::RequestAdapterOptions<'_, '_>,
) -> Pin<Box<dyn RequestAdapterFuture>> {
let requested_backends = self.requested_backends;
let wasi_options = requested_backends
.contains(BACKEND)
.then(|| GpuRequestAdapterOptions {
feature_level: None,
power_preference: convert::power_preference_to_wasi(options.power_preference),
force_fallback_adapter: Some(options.force_fallback_adapter),
xr_compatible: None,
});
let gpu = Arc::clone(&self.gpu);
Box::pin(async move {
let adapter = match wasi_options {
Some(wasi_options) => gpu.request_adapter(Some(wasi_options)).await,
None => None,
};
adapter
.map(|adapter| DispatchAdapter::custom(WasdkAdapter::new(adapter)))
.ok_or_else(|| no_adapter(requested_backends))
})
}
fn poll_all_devices(&self, _force_wait: bool) -> bool {
true
}
fn wgsl_language_features(&self) -> WgslLanguageFeatures {
let supported = self.gpu.wgsl_language_features();
WGSL_LANGUAGE_FEATURES
.iter()
.filter(|(_, name)| supported.has(name))
.fold(WgslLanguageFeatures::empty(), |features, (bit, _)| {
features | bit.clone()
})
}
fn enumerate_adapters(&self, backends: Backends) -> Pin<Box<dyn EnumerateAdapterFuture>> {
if !backends.contains(BACKEND) {
return Box::pin(async { Vec::new() });
}
let adapter = self.request_adapter(&wgpu::RequestAdapterOptions::default());
Box::pin(async move { adapter.await.into_iter().collect() })
}
}
fn no_adapter(requested_backends: Backends) -> RequestAdapterError {
RequestAdapterError::NotFound {
active_backends: BACKEND,
requested_backends,
supported_backends: BACKEND,
no_fallback_backends: Backends::empty(),
no_adapter_backends: BACKEND,
incompatible_surface_backends: Backends::empty(),
}
}