diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a463c89bb..a647a0662a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -88,6 +88,7 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). ### Bug Fixes - Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123) +- Fix JS `TypeError` exception in `Instance::request_adapter` when browser doesn't support WebGPU but `wgpu` not compiled with `webgl` support. By @bgr360 in [#6197](https://github.com/gfx-rs/wgpu/pull/6197). #### Naga @@ -126,6 +127,10 @@ By @bradwerth [#6216](https://github.com/gfx-rs/wgpu/pull/6216). - Change the inconsistent `DropGuard` based API on Vulkan and GLES to a consistent, callback-based one. By @jerzywilczek in [#6164](https://github.com/gfx-rs/wgpu/pull/6164) +### Documentation + +- Removed some OpenGL and Vulkan references from `wgpu-types` documentation. Fixed Storage texel types in examples. By @Nelarius in [#6271](https://github.com/gfx-rs/wgpu/pull/6271) + ### Dependency Updates #### GLES diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 7c572951fe..9e7e479121 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -568,7 +568,7 @@ bitflags::bitflags! { /// may also create uniform arrays of storage textures. /// /// ex. - /// - `var textures: array, 10>` (WGSL) + /// - `var textures: array, 10>` (WGSL) /// - `uniform image2D textures[10]` (GLSL) /// /// This capability allows them to exist and to be indexed by dynamically uniform @@ -1959,12 +1959,13 @@ impl TextureViewDimension { /// Alpha blend factor. /// -/// Alpha blending is very complicated: see the OpenGL or Vulkan spec for more information. -/// /// Corresponds to [WebGPU `GPUBlendFactor`]( -/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor). -/// Values using S1 requires [`Features::DUAL_SOURCE_BLENDING`] and can only be -/// used with the first render target. +/// https://gpuweb.github.io/gpuweb/#enumdef-gpublendfactor). Values using `Src1` +/// require [`Features::DUAL_SOURCE_BLENDING`] and can only be used with the first +/// render target. +/// +/// For further details on how the blend factors are applied, see the analogous +/// functionality in OpenGL: . #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -2024,10 +2025,11 @@ impl BlendFactor { /// Alpha blend operation. /// -/// Alpha blending is very complicated: see the OpenGL or Vulkan spec for more information. -/// /// Corresponds to [WebGPU `GPUBlendOperation`]( /// https://gpuweb.github.io/gpuweb/#enumdef-gpublendoperation). +/// +/// For further details on how the blend operations are applied, see +/// the analogous functionality in OpenGL: . #[repr(C)] #[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] @@ -2102,8 +2104,6 @@ impl Default for BlendComponent { /// Describe the blend state of a render pipeline, /// within [`ColorTargetState`]. /// -/// See the OpenGL or Vulkan spec for more information. -/// /// Corresponds to [WebGPU `GPUBlendState`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpublendstate). #[repr(C)] @@ -6547,7 +6547,7 @@ pub enum StorageTextureAccess { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: @@ -6564,7 +6564,7 @@ pub enum StorageTextureAccess { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: @@ -6581,7 +6581,7 @@ pub enum StorageTextureAccess { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: @@ -6705,8 +6705,8 @@ pub enum BindingType { /// Dimension of the texture view that is going to be sampled. view_dimension: TextureViewDimension, /// True if the texture has a sample count greater than 1. If this is true, - /// the texture must be read from shaders with `texture1DMS`, `texture2DMS`, or `texture3DMS`, - /// depending on `dimension`. + /// the texture must be declared as `texture_multisampled_2d` or + /// `texture_depth_multisampled_2d` in the shader, and read using `textureLoad`. multisampled: bool, }, /// A storage texture. @@ -6714,15 +6714,16 @@ pub enum BindingType { /// Example WGSL syntax: /// ```rust,ignore /// @group(0) @binding(0) - /// var my_storage_image: texture_storage_2d; + /// var my_storage_image: texture_storage_2d; /// ``` /// /// Example GLSL syntax: /// ```cpp,ignore /// layout(set=0, binding=0, r32f) writeonly uniform image2D myStorageImage; /// ``` - /// Note that the texture format must be specified in the shader as well. - /// A list of valid formats can be found in the specification here: + /// Note that the texture format must be specified in the shader, along with the + /// access mode. For WGSL, the format must be one of the enumerants in the list + /// of [storage texel formats](https://gpuweb.github.io/gpuweb/wgsl/#storage-texel-formats). /// /// Corresponds to [WebGPU `GPUStorageTextureBindingLayout`]( /// https://gpuweb.github.io/gpuweb/#dictdef-gpustoragetexturebindinglayout). diff --git a/wgpu/src/api/compute_pipeline.rs b/wgpu/src/api/compute_pipeline.rs index 50f17122ea..18d4e904e4 100644 --- a/wgpu/src/api/compute_pipeline.rs +++ b/wgpu/src/api/compute_pipeline.rs @@ -20,6 +20,10 @@ super::impl_partialeq_eq_hash!(ComputePipeline); impl ComputePipeline { /// Get an object representing the bind group layout at a given index. + /// + /// If this pipeline was created with a [default layout][ComputePipelineDescriptor::layout], + /// then bind groups created with the returned `BindGroupLayout` can only be used with this + /// pipeline. pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout { let context = Arc::clone(&self.context); let data = self @@ -48,6 +52,25 @@ pub struct ComputePipelineDescriptor<'a> { /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// The layout of bind groups for this pipeline. + /// + /// If this is set, then [`Device::create_compute_pipeline`] will raise a validation error if + /// the layout doesn't match what the shader module(s) expect. + /// + /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`] + /// pipelines guarantees that you don't have to rebind any resources when switching between + /// those pipelines. + /// + /// ## Default pipeline layout + /// + /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead. + /// The default layout is deduced from the shader modules. + /// + /// You can use [`ComputePipeline::get_bind_group_layout`] to create bind groups for use with + /// the default layout. However, these bind groups cannot be used with any other pipelines. This + /// is convenient for simple pipelines, but using an explicit layout is recommended in most + /// cases. + /// + /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout pub layout: Option<&'a PipelineLayout>, /// The compiled shader module for this stage. pub module: &'a ShaderModule, diff --git a/wgpu/src/api/instance.rs b/wgpu/src/api/instance.rs index 33a7b0f649..af6775b86b 100644 --- a/wgpu/src/api/instance.rs +++ b/wgpu/src/api/instance.rs @@ -95,7 +95,7 @@ impl Instance { /// [`Backends::BROWSER_WEBGPU`] takes a special role: /// If it is set and WebGPU support is detected, this instance will *only* be able to create /// WebGPU adapters. If you instead want to force use of WebGL, either - /// disable the `webgpu` compile-time feature or do add the [`Backends::BROWSER_WEBGPU`] + /// disable the `webgpu` compile-time feature or don't add the [`Backends::BROWSER_WEBGPU`] /// flag to the the `instance_desc`'s `backends` field. /// If it is set and WebGPU support is *not* detected, the instance will use wgpu-core /// to create adapters. Meaning that if the `webgl` feature is enabled, it is able to create @@ -118,8 +118,9 @@ impl Instance { { let is_only_available_backend = !cfg!(wgpu_core); let requested_webgpu = _instance_desc.backends.contains(Backends::BROWSER_WEBGPU); - let support_webgpu = - crate::backend::get_browser_gpu_property().map_or(false, |gpu| !gpu.is_undefined()); + let support_webgpu = crate::backend::get_browser_gpu_property() + .map(|maybe_gpu| maybe_gpu.is_some()) + .unwrap_or(false); if is_only_available_backend || (requested_webgpu && support_webgpu) { return Self { diff --git a/wgpu/src/api/pipeline_layout.rs b/wgpu/src/api/pipeline_layout.rs index 2b89d2b7aa..20538dd9e7 100644 --- a/wgpu/src/api/pipeline_layout.rs +++ b/wgpu/src/api/pipeline_layout.rs @@ -40,8 +40,8 @@ pub struct PipelineLayoutDescriptor<'a> { /// "set = 0", second entry will provide all the bindings for "set = 1" etc. pub bind_group_layouts: &'a [&'a BindGroupLayout], /// Set of push constant ranges this pipeline uses. Each shader stage that uses push constants - /// must define the range in push constant memory that corresponds to its single `layout(push_constant)` - /// uniform block. + /// must define the range in push constant memory that corresponds to its single `var` + /// buffer. /// /// If this array is non-empty, the [`Features::PUSH_CONSTANTS`] must be enabled. pub push_constant_ranges: &'a [PushConstantRange], diff --git a/wgpu/src/api/render_pipeline.rs b/wgpu/src/api/render_pipeline.rs index 1893f7c7b2..3009cde1da 100644 --- a/wgpu/src/api/render_pipeline.rs +++ b/wgpu/src/api/render_pipeline.rs @@ -28,6 +28,9 @@ impl Drop for RenderPipeline { impl RenderPipeline { /// Get an object representing the bind group layout at a given index. + /// + /// If this pipeline was created with a [default layout][RenderPipelineDescriptor::layout], then + /// bind groups created with the returned `BindGroupLayout` can only be used with this pipeline. pub fn get_bind_group_layout(&self, index: u32) -> BindGroupLayout { let context = Arc::clone(&self.context); let data = self @@ -121,6 +124,24 @@ pub struct RenderPipelineDescriptor<'a> { /// Debug label of the pipeline. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// The layout of bind groups for this pipeline. + /// + /// If this is set, then [`Device::create_render_pipeline`] will raise a validation error if + /// the layout doesn't match what the shader module(s) expect. + /// + /// Using the same [`PipelineLayout`] for many [`RenderPipeline`] or [`ComputePipeline`] + /// pipelines guarantees that you don't have to rebind any resources when switching between + /// those pipelines. + /// + /// ## Default pipeline layout + /// + /// If `layout` is `None`, then the pipeline has a [default layout] created and used instead. + /// The default layout is deduced from the shader modules. + /// + /// You can use [`RenderPipeline::get_bind_group_layout`] to create bind groups for use with the + /// default layout. However, these bind groups cannot be used with any other pipelines. This is + /// convenient for simple pipelines, but using an explicit layout is recommended in most cases. + /// + /// [default layout]: https://www.w3.org/TR/webgpu/#default-pipeline-layout pub layout: Option<&'a PipelineLayout>, /// The compiled vertex stage, its entry point, and the input buffers layout. pub vertex: VertexState<'a>, diff --git a/wgpu/src/backend/webgpu.rs b/wgpu/src/backend/webgpu.rs index a921b45652..05f6d50194 100644 --- a/wgpu/src/backend/webgpu.rs +++ b/wgpu/src/backend/webgpu.rs @@ -1,5 +1,6 @@ #![allow(clippy::type_complexity)] +mod defined_non_null_js_value; mod ext_bindings; mod webgpu_sys; @@ -22,13 +23,16 @@ use crate::{ CompilationInfo, SurfaceTargetUnsafe, UncapturedErrorHandler, }; +use defined_non_null_js_value::DefinedNonNullJsValue; + // We need to make a wrapper for some of the handle types returned by the web backend to make them // implement `Send` and `Sync` to match native. // // SAFETY: All webgpu handle types in wasm32 are internally a `JsValue`, and `JsValue` is neither -// Send nor Sync. Currently, wasm32 has no threading support so implementing `Send` or `Sync` for a -// type is (for now) harmless. Eventually wasm32 will support threading, and depending on how this -// is integrated (or not integrated) with values like those in webgpu, this may become unsound. +// Send nor Sync. Currently, wasm32 has no threading support by default, so implementing `Send` or +// `Sync` for a type is harmless. However, nightly Rust supports compiling wasm with experimental +// threading support via `--target-features`. If `wgpu` is being compiled with those features, we do +// not implement `Send` and `Sync` on the webgpu handle types. #[derive(Clone, Debug)] pub(crate) struct Sendable(T); @@ -37,7 +41,10 @@ unsafe impl Send for Sendable {} #[cfg(send_sync)] unsafe impl Sync for Sendable {} -pub(crate) struct ContextWebGpu(webgpu_sys::Gpu); +pub(crate) struct ContextWebGpu { + /// `None` if browser does not advertise support for WebGPU. + gpu: Option>, +} #[cfg(send_sync)] unsafe impl Send for ContextWebGpu {} #[cfg(send_sync)] @@ -188,6 +195,36 @@ impl MakeSendFuture { #[cfg(send_sync)] unsafe impl Send for MakeSendFuture {} +/// Wraps a future that returns `Option` and adds the ability to immediately +/// return None. +pub(crate) struct OptionFuture(Option); + +impl>, T> Future for OptionFuture { + type Output = Option; + + fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll { + // This is safe because we have no Drop implementation to violate the Pin requirements and + // do not provide any means of moving the inner future. + unsafe { + let this = self.get_unchecked_mut(); + match &mut this.0 { + Some(future) => Pin::new_unchecked(future).poll(cx), + None => task::Poll::Ready(None), + } + } + } +} + +impl OptionFuture { + fn some(future: F) -> Self { + Self(Some(future)) + } + + fn none() -> Self { + Self(None) + } +} + fn map_texture_format(texture_format: wgt::TextureFormat) -> webgpu_sys::GpuTextureFormat { use webgpu_sys::GpuTextureFormat as tf; use wgt::TextureFormat; @@ -1045,27 +1082,34 @@ pub enum Canvas { Offscreen(web_sys::OffscreenCanvas), } -/// Returns the browsers gpu object or `None` if the current context is neither the main thread nor a dedicated worker. +#[derive(Debug, Clone, Copy)] +pub struct BrowserGpuPropertyInaccessible; + +/// Returns the browser's gpu object or `Err(BrowserGpuPropertyInaccessible)` if +/// the current context is neither the main thread nor a dedicated worker. /// -/// If WebGPU is not supported, the Gpu property is `undefined` (but *not* necessarily `None`). +/// If WebGPU is not supported, the Gpu property is `undefined`, and so this +/// function will return `Ok(None)`. /// /// See: /// * /// * -pub fn get_browser_gpu_property() -> Option { +pub fn get_browser_gpu_property( +) -> Result>, BrowserGpuPropertyInaccessible> { let global: Global = js_sys::global().unchecked_into(); - if !global.window().is_undefined() { + let maybe_undefined_gpu: webgpu_sys::Gpu = if !global.window().is_undefined() { let navigator = global.unchecked_into::().navigator(); - Some(ext_bindings::NavigatorGpu::gpu(&navigator)) + ext_bindings::NavigatorGpu::gpu(&navigator) } else if !global.worker().is_undefined() { let navigator = global .unchecked_into::() .navigator(); - Some(ext_bindings::NavigatorGpu::gpu(&navigator)) + ext_bindings::NavigatorGpu::gpu(&navigator) } else { - None - } + return Err(BrowserGpuPropertyInaccessible); + }; + Ok(DefinedNonNullJsValue::new(maybe_undefined_gpu)) } impl crate::context::Context for ContextWebGpu { @@ -1097,9 +1141,11 @@ impl crate::context::Context for ContextWebGpu { type SubmissionIndexData = (); type PipelineCacheData = (); - type RequestAdapterFuture = MakeSendFuture< - wasm_bindgen_futures::JsFuture, - fn(JsFutureResult) -> Option, + type RequestAdapterFuture = OptionFuture< + MakeSendFuture< + wasm_bindgen_futures::JsFuture, + fn(JsFutureResult) -> Option, + >, >; type RequestDeviceFuture = MakeSendFuture< wasm_bindgen_futures::JsFuture, @@ -1116,12 +1162,13 @@ impl crate::context::Context for ContextWebGpu { >; fn init(_instance_desc: wgt::InstanceDescriptor) -> Self { - let Some(gpu) = get_browser_gpu_property() else { + let Ok(gpu) = get_browser_gpu_property() else { panic!( "Accessing the GPU is only supported on the main thread or from a dedicated worker" ); }; - ContextWebGpu(gpu) + + ContextWebGpu { gpu } } unsafe fn instance_create_surface( @@ -1191,12 +1238,16 @@ impl crate::context::Context for ContextWebGpu { if let Some(mapped_pref) = mapped_power_preference { mapped_options.power_preference(mapped_pref); } - let adapter_promise = self.0.request_adapter_with_options(&mapped_options); - - MakeSendFuture::new( - wasm_bindgen_futures::JsFuture::from(adapter_promise), - future_request_adapter, - ) + if let Some(gpu) = &self.gpu { + let adapter_promise = gpu.request_adapter_with_options(&mapped_options); + OptionFuture::some(MakeSendFuture::new( + wasm_bindgen_futures::JsFuture::from(adapter_promise), + future_request_adapter, + )) + } else { + // Gpu is undefined; WebGPU is not supported in this browser. + OptionFuture::none() + } } fn adapter_request_device( @@ -1317,7 +1368,11 @@ impl crate::context::Context for ContextWebGpu { let mut mapped_formats = formats.iter().map(|format| map_texture_format(*format)); // Preferred canvas format will only be either "rgba8unorm" or "bgra8unorm". // https://www.w3.org/TR/webgpu/#dom-gpu-getpreferredcanvasformat - let preferred_format = self.0.get_preferred_canvas_format(); + let gpu = self + .gpu + .as_ref() + .expect("Caller could not have created an adapter if gpu is undefined."); + let preferred_format = gpu.get_preferred_canvas_format(); if let Some(index) = mapped_formats.position(|format| format == preferred_format) { formats.swap(0, index); } diff --git a/wgpu/src/backend/webgpu/defined_non_null_js_value.rs b/wgpu/src/backend/webgpu/defined_non_null_js_value.rs new file mode 100644 index 0000000000..fc5a8737ef --- /dev/null +++ b/wgpu/src/backend/webgpu/defined_non_null_js_value.rs @@ -0,0 +1,46 @@ +use std::ops::{Deref, DerefMut}; + +use wasm_bindgen::JsValue; + +/// Derefs to a [`JsValue`] that's known not to be `undefined` or `null`. +#[derive(Debug)] +pub struct DefinedNonNullJsValue(T); + +impl DefinedNonNullJsValue +where + T: AsRef, +{ + pub fn new(value: T) -> Option { + if value.as_ref().is_undefined() || value.as_ref().is_null() { + None + } else { + Some(Self(value)) + } + } +} + +impl Deref for DefinedNonNullJsValue { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl DerefMut for DefinedNonNullJsValue { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl AsRef for DefinedNonNullJsValue { + fn as_ref(&self) -> &T { + &self.0 + } +} + +impl AsMut for DefinedNonNullJsValue { + fn as_mut(&mut self) -> &mut T { + &mut self.0 + } +}