diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml
index 1bf9e178715..68bd2db25fe 100644
--- a/.github/workflows/publish.yml
+++ b/.github/workflows/publish.yml
@@ -24,7 +24,7 @@ jobs:
target: wasm32-unknown-unknown
- name: Install wasm-bindgen-cli
- run: cargo install wasm-bindgen-cli --version=0.2.81
+ run: cargo install wasm-bindgen-cli --version=0.2.83
- name: Build WebGPU examples
run: cargo build --release --target wasm32-unknown-unknown --examples
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7f41abc0551..61e9a6c63a5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -123,6 +123,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
- The `strict_assert` family of macros was moved to `wgpu-types`. By @i509VCB in [#3051](https://github.com/gfx-rs/wgpu/pull/3051)
- Add missing `DEPTH_BIAS_CLAMP` and `FULL_DRAW_INDEX_UINT32` downlevel flags. By @teoxoy in [#3316](https://github.com/gfx-rs/wgpu/pull/3316)
- Make `ObjectId` structure and invariants idiomatic. By @teoxoy in [#3347](https://github.com/gfx-rs/wgpu/pull/3347)
+- Add validation in accordance with WebGPU `GPUSamplerDescriptor` valid usage for `lodMinClamp` and `lodMaxClamp`. By @James2022-rgb in [#3353](https://github.com/gfx-rs/wgpu/pull/3353)
#### WebGPU
@@ -172,6 +173,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
- Check for invalid bitflag bits in wgpu-core and allow them to be captured/replayed by @nical in (#3229)[https://github.com/gfx-rs/wgpu/pull/3229]
- Evaluate `gfx_select!`'s `#[cfg]` conditions at the right time. By @jimblandy in [#3253](https://github.com/gfx-rs/wgpu/pull/3253)
- Improve error messages when binding bind group with dynamic offsets. By @cwfitzgerald in [#3294](https://github.com/gfx-rs/wgpu/pull/3294)
+- Allow non-filtering sampling of integer textures. By @JMS55 in [#3362](https://github.com/gfx-rs/wgpu/pull/3362).
#### Metal
- Fix texture view creation with full-resource views when using an explicit `mip_level_count` or `array_layer_count`. By @cwfitzgerald in [#3323](https://github.com/gfx-rs/wgpu/pull/3323)
@@ -184,6 +186,11 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non
- Fixed WebGL not displaying srgb targets correctly if a non-screen filling viewport was previously set. By @Wumpf in [#3093](https://github.com/gfx-rs/wgpu/pull/3093)
- Fix disallowing multisampling for float textures if otherwise supported. By @Wumpf in [#3183](https://github.com/gfx-rs/wgpu/pull/3183)
+- Fix a panic when creating a pipeline with opaque types other than samplers (images and atomic counters). By @James2022-rgb in [#3361](https://github.com/gfx-rs/wgpu/pull/3361)
+
+#### Vulkan
+
+- Document and improve extension detection. By @teoxoy in [#3327](https://github.com/gfx-rs/wgpu/pull/3327)
#### deno-webgpu
diff --git a/Cargo.lock b/Cargo.lock
index cb01f676edc..981c1ca4453 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -628,6 +628,7 @@ name = "deno_webgpu"
version = "0.81.0"
dependencies = [
"deno_core",
+ "raw-window-handle 0.5.0",
"serde",
"tokio",
"wgpu-core",
diff --git a/deno_webgpu/Cargo.toml b/deno_webgpu/Cargo.toml
index 4a47a9418e8..628adbd6c35 100644
--- a/deno_webgpu/Cargo.toml
+++ b/deno_webgpu/Cargo.toml
@@ -10,11 +10,15 @@ readme = "README.md"
repository.workspace = true
description = "WebGPU implementation for Deno"
+[features]
+surface = ["wgpu-core/raw-window-handle", "dep:raw-window-handle"]
+
[dependencies]
deno_core.workspace = true
serde = { workspace = true, features = ["derive"] }
tokio = { workspace = true, features = ["full"] }
wgpu-types = { workspace = true, features = ["trace", "replay", "serde"] }
+raw-window-handle = { workspace = true, optional = true }
[dependencies.wgpu-core]
workspace = true
diff --git a/deno_webgpu/src/01_webgpu.js b/deno_webgpu/src/01_webgpu.js
index 373fe58a8b4..d857bd2b429 100644
--- a/deno_webgpu/src/01_webgpu.js
+++ b/deno_webgpu/src/01_webgpu.js
@@ -188,10 +188,13 @@
}
}
+ const illegalConstructorKey = Symbol("illegalConstructorKey");
class GPUError extends Error {
- constructor() {
+ constructor(key = null) {
super();
- webidl.illegalConstructor();
+ if (key !== illegalConstructorKey) {
+ webidl.illegalConstructor();
+ }
}
[_message];
@@ -212,7 +215,9 @@
prefix,
context: "Argument 1",
});
- super(message);
+ super(illegalConstructorKey);
+ this[webidl.brand] = webidl.brand;
+ this[_message] = message;
}
}
const GPUValidationErrorPrototype = GPUValidationError.prototype;
@@ -226,7 +231,9 @@
prefix,
context: "Argument 1",
});
- super(message);
+ super(illegalConstructorKey);
+ this[webidl.brand] = webidl.brand;
+ this[_message] = message;
}
}
const GPUOutOfMemoryErrorPrototype = GPUOutOfMemoryError.prototype;
@@ -347,7 +354,7 @@
rid,
adapter: this,
features: createGPUSupportedFeatures(features),
- limits: createGPUSupportedFeatures(limits),
+ limits: createGPUSupportedLimits(limits),
});
return createGPUDevice(
descriptor.label,
@@ -5251,6 +5258,9 @@
const GPUQuerySetPrototype = GPUQuerySet.prototype;
window.__bootstrap.webgpu = {
+ _device,
+ assertDevice,
+ createGPUTexture,
gpu: webidl.createBranded(GPU),
GPU,
GPUAdapter,
diff --git a/deno_webgpu/src/03_surface.js b/deno_webgpu/src/03_surface.js
new file mode 100644
index 00000000000..bd3da8cd37a
--- /dev/null
+++ b/deno_webgpu/src/03_surface.js
@@ -0,0 +1,135 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+///
+///
+///
+///
+
+"use strict";
+
+((window) => {
+ const core = window.Deno.core;
+ const ops = core.ops;
+ const webidl = window.__bootstrap.webidl;
+ const { Symbol } = window.__bootstrap.primordials;
+ const { _device, assertDevice, createGPUTexture } = window.__bootstrap.webgpu;
+
+ const _surfaceRid = Symbol("[[surfaceRid]]");
+ const _configuration = Symbol("[[configuration]]");
+ const _canvas = Symbol("[[canvas]]");
+ const _currentTexture = Symbol("[[currentTexture]]");
+ class GPUCanvasContext {
+ /** @type {number} */
+ [_surfaceRid];
+ /** @type {InnerGPUDevice} */
+ [_device];
+ [_configuration];
+ [_canvas];
+ /** @type {GPUTexture | undefined} */
+ [_currentTexture];
+
+ get canvas() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ return this[_canvas];
+ }
+
+ constructor() {
+ webidl.illegalConstructor();
+ }
+
+ configure(configuration) {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ const prefix = "Failed to execute 'configure' on 'GPUCanvasContext'";
+ webidl.requiredArguments(arguments.length, 1, { prefix });
+ configuration = webidl.converters.GPUCanvasConfiguration(configuration, {
+ prefix,
+ context: "Argument 1",
+ });
+
+ this[_device] = configuration.device[_device];
+ this[_configuration] = configuration;
+ const device = assertDevice(this, { prefix, context: "configuration.device" });
+
+ const { err } = ops.op_webgpu_surface_configure({
+ surfaceRid: this[_surfaceRid],
+ deviceRid: device.rid,
+ format: configuration.format,
+ usage: configuration.usage,
+ width: configuration.width,
+ height: configuration.height,
+ alphaMode: configuration.alphaMode,
+ });
+
+ device.pushError(err);
+ }
+
+ unconfigure() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+
+ this[_configuration] = null;
+ this[_device] = null;
+ }
+
+ getCurrentTexture() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ const prefix = "Failed to execute 'getCurrentTexture' on 'GPUCanvasContext'";
+
+ if (this[_configuration] === null) {
+ throw new DOMException("context is not configured.", "InvalidStateError");
+ }
+
+ const device = assertDevice(this, { prefix, context: "this" });
+
+ if (this[_currentTexture]) {
+ return this[_currentTexture];
+ }
+
+ const { rid } = ops.op_webgpu_surface_get_current_texture(device.rid, this[_surfaceRid]);
+
+ const texture = createGPUTexture(
+ {
+ size: {
+ width: this[_configuration].width,
+ height: this[_configuration].height,
+ depthOrArrayLayers: 1,
+ },
+ mipLevelCount: 1,
+ sampleCount: 1,
+ dimension: "2d",
+ format: this[_configuration].format,
+ usage: this[_configuration].usage,
+ },
+ device,
+ rid,
+ );
+ device.trackResource(texture);
+ this[_currentTexture] = texture;
+ return texture;
+ }
+
+ // Extended from spec. Required to present the texture; browser don't need this.
+ present() {
+ webidl.assertBranded(this, GPUCanvasContextPrototype);
+ const prefix = "Failed to execute 'present' on 'GPUCanvasContext'";
+ const device = assertDevice(this[_currentTexture], { prefix, context: "this" });
+ ops.op_webgpu_surface_present(device.rid, this[_surfaceRid]);
+ this[_currentTexture].destroy();
+ this[_currentTexture] = undefined;
+ }
+ }
+ const GPUCanvasContextPrototype = GPUCanvasContext.prototype;
+
+ function createCanvasContext(options) {
+ const canvasContext = webidl.createBranded(GPUCanvasContext);
+ canvasContext[_surfaceRid] = options.surfaceRid;
+ canvasContext[_canvas] = options.canvas;
+ return canvasContext;
+ }
+
+ window.__bootstrap.webgpu = {
+ ...window.__bootstrap.webgpu,
+ GPUCanvasContext,
+ createCanvasContext,
+ };
+})(this);
diff --git a/deno_webgpu/src/04_surface_idl_types.js b/deno_webgpu/src/04_surface_idl_types.js
new file mode 100644
index 00000000000..942c2a5b2b1
--- /dev/null
+++ b/deno_webgpu/src/04_surface_idl_types.js
@@ -0,0 +1,77 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+// @ts-check
+///
+///
+///
+///
+
+"use strict";
+
+((window) => {
+ const webidl = window.__bootstrap.webidl;
+ const { GPUTextureUsage } = window.__bootstrap.webgpu;
+
+ // ENUM: GPUCanvasAlphaMode
+ webidl.converters["GPUCanvasAlphaMode"] = webidl.createEnumConverter(
+ "GPUCanvasAlphaMode",
+ [
+ "opaque",
+ "premultiplied",
+ ],
+ );
+
+ // NON-SPEC: ENUM: GPUPresentMode
+ webidl.converters["GPUPresentMode"] = webidl.createEnumConverter(
+ "GPUPresentMode",
+ [
+ "autoVsync",
+ "autoNoVsync",
+ "fifo",
+ "fifoRelaxed",
+ "immediate",
+ "mailbox",
+ ],
+ );
+
+ // DICT: GPUCanvasConfiguration
+ const dictMembersGPUCanvasConfiguration = [
+ { key: "device", converter: webidl.converters.GPUDevice, required: true },
+ {
+ key: "format",
+ converter: webidl.converters.GPUTextureFormat,
+ required: true,
+ },
+ {
+ key: "usage",
+ converter: webidl.converters["GPUTextureUsageFlags"],
+ defaultValue: GPUTextureUsage.RENDER_ATTACHMENT,
+ },
+ {
+ key: "alphaMode",
+ converter: webidl.converters["GPUCanvasAlphaMode"],
+ defaultValue: "opaque",
+ },
+
+ // Extended from spec
+ {
+ key: "presentMode",
+ converter: webidl.converters["GPUPresentMode"],
+ },
+ {
+ key: "width",
+ converter: webidl.converters["long"],
+ required: true,
+ },
+ {
+ key: "height",
+ converter: webidl.converters["long"],
+ required: true,
+ },
+ ];
+ webidl.converters["GPUCanvasConfiguration"] = webidl
+ .createDictionaryConverter(
+ "GPUCanvasConfiguration",
+ dictMembersGPUCanvasConfiguration,
+ );
+})(this);
diff --git a/deno_webgpu/src/error.rs b/deno_webgpu/src/error.rs
index 541fa9d9087..f9903579f9e 100644
--- a/deno_webgpu/src/error.rs
+++ b/deno_webgpu/src/error.rs
@@ -23,6 +23,8 @@ use wgpu_core::device::DeviceError;
use wgpu_core::pipeline::CreateComputePipelineError;
use wgpu_core::pipeline::CreateRenderPipelineError;
use wgpu_core::pipeline::CreateShaderModuleError;
+#[cfg(feature = "surface")]
+use wgpu_core::present::ConfigureSurfaceError;
use wgpu_core::resource::BufferAccessError;
use wgpu_core::resource::CreateBufferError;
use wgpu_core::resource::CreateQuerySetError;
@@ -275,6 +277,13 @@ impl From for WebGpuError {
}
}
+#[cfg(feature = "surface")]
+impl From for WebGpuError {
+ fn from(err: ConfigureSurfaceError) -> Self {
+ WebGpuError::Validation(fmt_err(&err))
+ }
+}
+
#[derive(Debug)]
pub struct DomExceptionOperationError {
pub msg: String,
diff --git a/deno_webgpu/src/lib.rs b/deno_webgpu/src/lib.rs
index 287e3409204..bdd64b3c5fb 100644
--- a/deno_webgpu/src/lib.rs
+++ b/deno_webgpu/src/lib.rs
@@ -27,13 +27,22 @@ mod macros {
macro_rules! gfx_select {
($id:expr => $global:ident.$method:ident( $($param:expr),* )) => {
match $id.backend() {
- #[cfg(not(target_os = "macos"))]
+ #[cfg(any(
+ all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")),
+ feature = "vulkan-portability"
+ ))]
wgpu_types::Backend::Vulkan => $global.$method::( $($param),* ),
- #[cfg(target_os = "macos")]
+ #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))]
wgpu_types::Backend::Metal => $global.$method::( $($param),* ),
- #[cfg(windows)]
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
wgpu_types::Backend::Dx12 => $global.$method::( $($param),* ),
- #[cfg(all(unix, not(target_os = "macos")))]
+ #[cfg(all(not(target_arch = "wasm32"), windows))]
+ wgpu_types::Backend::Dx11 => $global.$method::( $($param),* ),
+ #[cfg(any(
+ all(unix, not(target_os = "macos"), not(target_os = "ios")),
+ feature = "angle",
+ target_arch = "wasm32"
+ ))]
wgpu_types::Backend::Gl => $global.$method::( $($param),+ ),
other => panic!("Unexpected backend {:?}", other),
}
@@ -67,6 +76,8 @@ pub mod queue;
pub mod render_pass;
pub mod sampler;
pub mod shader;
+#[cfg(feature = "surface")]
+pub mod surface;
pub mod texture;
pub struct Unstable(pub bool);
@@ -82,7 +93,7 @@ fn check_unstable(state: &OpState, api_name: &str) {
}
}
-type Instance = wgpu_core::hub::Global;
+pub type Instance = wgpu_core::hub::Global;
struct WebGpuAdapter(wgpu_core::id::AdapterId);
impl Resource for WebGpuAdapter {
diff --git a/deno_webgpu/src/surface.rs b/deno_webgpu/src/surface.rs
new file mode 100644
index 00000000000..8a476667d47
--- /dev/null
+++ b/deno_webgpu/src/surface.rs
@@ -0,0 +1,130 @@
+// Copyright 2018-2022 the Deno authors. All rights reserved. MIT license.
+
+use super::WebGpuResult;
+use deno_core::error::AnyError;
+use deno_core::include_js_files;
+use deno_core::op;
+use deno_core::Extension;
+use deno_core::OpState;
+use deno_core::Resource;
+use deno_core::ResourceId;
+use serde::Deserialize;
+use std::borrow::Cow;
+use wgpu_types::SurfaceStatus;
+
+pub fn init_surface(unstable: bool) -> Extension {
+ Extension::builder()
+ .js(include_js_files!(
+ prefix "deno:deno_webgpu",
+ "03_surface.js",
+ "04_surface_idl_types.js",
+ ))
+ .ops(vec![
+ op_webgpu_surface_configure::decl(),
+ op_webgpu_surface_get_current_texture::decl(),
+ op_webgpu_surface_present::decl(),
+ ])
+ .state(move |state| {
+ // TODO: check & possibly streamline this
+ // Unstable might be able to be OpMiddleware
+ // let unstable_checker = state.borrow::();
+ // let unstable = unstable_checker.unstable;
+ state.put(super::Unstable(unstable));
+ Ok(())
+ })
+ .build()
+}
+
+pub struct WebGpuSurface(pub wgpu_core::id::SurfaceId);
+impl Resource for WebGpuSurface {
+ fn name(&self) -> Cow {
+ "webGPUSurface".into()
+ }
+}
+
+#[derive(Deserialize)]
+#[serde(rename_all = "camelCase")]
+pub struct SurfaceConfigureArgs {
+ surface_rid: ResourceId,
+ device_rid: ResourceId,
+ format: wgpu_types::TextureFormat,
+ usage: u32,
+ width: u32,
+ height: u32,
+ present_mode: Option,
+ alpha_mode: wgpu_types::CompositeAlphaMode,
+}
+
+#[op]
+pub fn op_webgpu_surface_configure(
+ state: &mut OpState,
+ args: SurfaceConfigureArgs,
+) -> Result {
+ let instance = state.borrow::();
+ let device_resource = state
+ .resource_table
+ .get::(args.device_rid)?;
+ let device = device_resource.0;
+ let surface_resource = state
+ .resource_table
+ .get::(args.surface_rid)?;
+ let surface = surface_resource.0;
+
+ let conf = wgpu_types::SurfaceConfiguration {
+ usage: wgpu_types::TextureUsages::from_bits_truncate(args.usage),
+ format: args.format,
+ width: args.width,
+ height: args.height,
+ present_mode: args.present_mode.unwrap_or_default(),
+ alpha_mode: args.alpha_mode,
+ };
+
+ let err = gfx_select!(device => instance.surface_configure(surface, device, &conf));
+
+ Ok(WebGpuResult::maybe_err(err))
+}
+
+#[op]
+pub fn op_webgpu_surface_get_current_texture(
+ state: &mut OpState,
+ device_rid: ResourceId,
+ surface_rid: ResourceId,
+) -> Result {
+ let instance = state.borrow::();
+ let device_resource = state
+ .resource_table
+ .get::(device_rid)?;
+ let device = device_resource.0;
+ let surface_resource = state.resource_table.get::(surface_rid)?;
+ let surface = surface_resource.0;
+
+ let output = gfx_select!(device => instance.surface_get_current_texture(surface, ()))?;
+
+ match output.status {
+ SurfaceStatus::Good | SurfaceStatus::Suboptimal => {
+ let id = output.texture_id.unwrap();
+ let rid = state.resource_table.add(crate::texture::WebGpuTexture(id));
+ Ok(WebGpuResult::rid(rid))
+ }
+ _ => Err(AnyError::msg("Invalid Surface Status")),
+ }
+}
+
+#[op]
+pub fn op_webgpu_surface_present(
+ state: &mut OpState,
+ device_rid: ResourceId,
+ surface_rid: ResourceId,
+) -> Result<(), AnyError> {
+ let instance = state.borrow::();
+ let device_resource = state
+ .resource_table
+ .get::(device_rid)?;
+ let device = device_resource.0;
+ let surface_resource = state.resource_table.get::(surface_rid)?;
+ let surface = surface_resource.0;
+
+ let _ = gfx_select!(device => instance.surface_present(surface))?;
+
+ Ok(())
+}
diff --git a/deno_webgpu/webgpu.idl b/deno_webgpu/webgpu.idl
index 30b99a67c86..7d5da0a8780 100644
--- a/deno_webgpu/webgpu.idl
+++ b/deno_webgpu/webgpu.idl
@@ -374,11 +374,6 @@ interface GPUExternalTexture {
};
GPUExternalTexture includes GPUObjectBase;
-dictionary GPUExternalTextureDescriptor : GPUObjectDescriptorBase {
- required HTMLVideoElement source;
- PredefinedColorSpace colorSpace = "srgb";
-};
-
[Exposed=(Window, DedicatedWorker), SecureContext]
interface GPUSampler {
};
@@ -1068,6 +1063,26 @@ enum GPUPipelineStatisticName {
"compute-shader-invocations",
};
+[Exposed=(Window, DedicatedWorker), SecureContext]
+interface GPUCanvasContext {
+ undefined configure(GPUCanvasConfiguration configuration);
+ undefined unconfigure();
+
+ GPUTexture getCurrentTexture();
+};
+
+enum GPUCanvasAlphaMode {
+ "opaque",
+ "premultiplied"
+};
+
+dictionary GPUCanvasConfiguration {
+ required GPUDevice device;
+ required GPUTextureFormat format;
+ GPUTextureUsageFlags usage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENT
+ GPUCanvasAlphaMode alphaMode = "opaque";
+};
+
enum GPUDeviceLostReason {
"destroyed"
};
diff --git a/player/Cargo.toml b/player/Cargo.toml
index 814ed26ae7b..0946dad9ddf 100644
--- a/player/Cargo.toml
+++ b/player/Cargo.toml
@@ -12,7 +12,7 @@ publish = false
[features]
angle = ["wgc/angle"]
-vulkan-portability = ["wgc/vulkan-portability"]
+vulkan-portability = ["wgc/vulkan"]
[dependencies]
env_logger.workspace = true
@@ -29,5 +29,17 @@ features = ["replay"]
workspace = true
features = ["replay", "raw-window-handle", "strict_asserts", "wgsl"]
+[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
+workspace = true
+features = ["metal"]
+
+[target.'cfg(windows)'.dependencies.wgc]
+workspace = true
+features = ["dx11", "dx12"]
+
+[target.'cfg(any(windows, all(unix, not(target_arch = "emscripten"), not(target_os = "ios"), not(target_os = "macos"))))'.dependencies.wgc]
+workspace = true
+features = ["vulkan"]
+
[dev-dependencies]
serde.workspace = true
diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml
index bc2c212fc7f..a282b4c6cce 100644
--- a/wgpu-core/Cargo.toml
+++ b/wgpu-core/Cargo.toml
@@ -45,7 +45,6 @@ serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]
id32 = []
# Enable `ShaderModuleSource::Wgsl`
wgsl = ["naga/wgsl-in"]
-vulkan-portability = ["hal/vulkan"]
# Features that are intended to work on all platforms.
portable_features = ["gles", "strict_asserts", "trace", "replay", "serial-pass", "id32", "wgsl"]
diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs
index 955e8b4c223..db691f2e55b 100644
--- a/wgpu-core/src/device/mod.rs
+++ b/wgpu-core/src/device/mod.rs
@@ -1170,6 +1170,12 @@ impl Device {
self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
}
+ if desc.lod_min_clamp < 0.0 || desc.lod_max_clamp < desc.lod_min_clamp {
+ return Err(resource::CreateSamplerError::InvalidLodClamp(
+ desc.lod_min_clamp..desc.lod_max_clamp,
+ ));
+ }
+
let lod_clamp = if desc.lod_min_clamp > 0.0 || desc.lod_max_clamp < 32.0 {
Some(desc.lod_min_clamp..desc.lod_max_clamp)
} else {
diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs
index e59ec0aaa16..38a50a62c75 100644
--- a/wgpu-core/src/resource.rs
+++ b/wgpu-core/src/resource.rs
@@ -701,6 +701,8 @@ pub struct Sampler {
pub enum CreateSamplerError {
#[error(transparent)]
Device(#[from] DeviceError),
+ #[error("invalid lod clamp lod_min_clamp:{} lod_max_clamp:{}, must satisfy lod_min_clamp >= 0 and lod_max_clamp >= lod_min_clamp ", .0.start, .0.end)]
+ InvalidLodClamp(Range),
#[error("invalid anisotropic clamp {0}, must be one of 1, 2, 4, 8 or 16")]
InvalidClamp(u8),
#[error("cannot create any more samplers")]
diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs
index d190cbbfd9d..dcc179fb8fd 100644
--- a/wgpu-core/src/validation.rs
+++ b/wgpu-core/src/validation.rs
@@ -212,10 +212,10 @@ pub enum BindingError {
#[derive(Clone, Debug, Error)]
pub enum FilteringError {
- #[error("integer textures can't be sampled")]
+ #[error("integer textures can't be sampled with a filtering sampler")]
Integer,
- #[error("non-filterable float texture")]
- NonFilterable,
+ #[error("non-filterable float textures can't be sampled with a filtering sampler")]
+ Float,
}
#[derive(Clone, Debug, Error)]
@@ -1049,27 +1049,22 @@ impl Interface {
assert!(texture_layout.visibility.contains(stage_bit));
assert!(sampler_layout.visibility.contains(stage_bit));
- let error = match texture_layout.ty {
- wgt::BindingType::Texture {
- sample_type: wgt::TextureSampleType::Float { filterable },
- ..
- } => match sampler_layout.ty {
- wgt::BindingType::Sampler(wgt::SamplerBindingType::Filtering)
- if !filterable =>
- {
- Some(FilteringError::NonFilterable)
- }
- _ => None,
- },
- wgt::BindingType::Texture {
- sample_type: wgt::TextureSampleType::Sint,
- ..
+ let sampler_filtering = matches!(
+ sampler_layout.ty,
+ wgt::BindingType::Sampler(wgt::SamplerBindingType::Filtering)
+ );
+ let texture_sample_type = match texture_layout.ty {
+ BindingType::Texture { sample_type, .. } => sample_type,
+ _ => unreachable!(),
+ };
+
+ let error = match (sampler_filtering, texture_sample_type) {
+ (true, wgt::TextureSampleType::Float { filterable: false }) => {
+ Some(FilteringError::Float)
}
- | wgt::BindingType::Texture {
- sample_type: wgt::TextureSampleType::Uint,
- ..
- } => Some(FilteringError::Integer),
- _ => None, // unreachable, really
+ (true, wgt::TextureSampleType::Sint) => Some(FilteringError::Integer),
+ (true, wgt::TextureSampleType::Uint) => Some(FilteringError::Integer),
+ _ => None,
};
if let Some(error) = error {
diff --git a/wgpu-hal/src/gles/conv.rs b/wgpu-hal/src/gles/conv.rs
index 93f015363f5..14d30e93089 100644
--- a/wgpu-hal/src/gles/conv.rs
+++ b/wgpu-hal/src/gles/conv.rs
@@ -439,6 +439,52 @@ pub(super) fn is_sampler(glsl_uniform_type: u32) -> bool {
}
}
+pub(super) fn is_image(glsl_uniform_type: u32) -> bool {
+ match glsl_uniform_type {
+ glow::INT_IMAGE_1D
+ | glow::INT_IMAGE_1D_ARRAY
+ | glow::INT_IMAGE_2D
+ | glow::INT_IMAGE_2D_ARRAY
+ | glow::INT_IMAGE_2D_MULTISAMPLE
+ | glow::INT_IMAGE_2D_MULTISAMPLE_ARRAY
+ | glow::INT_IMAGE_2D_RECT
+ | glow::INT_IMAGE_3D
+ | glow::INT_IMAGE_CUBE
+ | glow::INT_IMAGE_CUBE_MAP_ARRAY
+ | glow::UNSIGNED_INT_IMAGE_1D
+ | glow::UNSIGNED_INT_IMAGE_1D_ARRAY
+ | glow::UNSIGNED_INT_IMAGE_2D
+ | glow::UNSIGNED_INT_IMAGE_2D_ARRAY
+ | glow::UNSIGNED_INT_IMAGE_2D_MULTISAMPLE
+ | glow::UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY
+ | glow::UNSIGNED_INT_IMAGE_2D_RECT
+ | glow::UNSIGNED_INT_IMAGE_3D
+ | glow::UNSIGNED_INT_IMAGE_CUBE
+ | glow::UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY
+ | glow::IMAGE_1D
+ | glow::IMAGE_1D_ARRAY
+ | glow::IMAGE_2D
+ | glow::IMAGE_2D_ARRAY
+ | glow::IMAGE_2D_MULTISAMPLE
+ | glow::IMAGE_2D_MULTISAMPLE_ARRAY
+ | glow::IMAGE_2D_RECT
+ | glow::IMAGE_3D
+ | glow::IMAGE_CUBE
+ | glow::IMAGE_CUBE_MAP_ARRAY => true,
+ _ => false,
+ }
+}
+
+pub(super) fn is_atomic_counter(glsl_uniform_type: u32) -> bool {
+ glsl_uniform_type == glow::UNSIGNED_INT_ATOMIC_COUNTER
+}
+
+pub(super) fn is_opaque_type(glsl_uniform_type: u32) -> bool {
+ is_sampler(glsl_uniform_type)
+ || is_image(glsl_uniform_type)
+ || is_atomic_counter(glsl_uniform_type)
+}
+
pub(super) fn uniform_byte_size(glsl_uniform_type: u32) -> u32 {
match glsl_uniform_type {
glow::FLOAT | glow::INT => 4,
@@ -448,6 +494,6 @@ pub(super) fn uniform_byte_size(glsl_uniform_type: u32) -> u32 {
glow::FLOAT_MAT2 => 16,
glow::FLOAT_MAT3 => 36,
glow::FLOAT_MAT4 => 64,
- _ => panic!("Unsupported uniform datatype!"),
+ _ => panic!("Unsupported uniform datatype! {glsl_uniform_type:#X}"),
}
}
diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs
index 2ec16b3c005..8d45ed1189f 100644
--- a/wgpu-hal/src/gles/device.rs
+++ b/wgpu-hal/src/gles/device.rs
@@ -380,7 +380,7 @@ impl super::Device {
let glow::ActiveUniform { utype, name, .. } =
unsafe { gl.get_active_uniform(program, uniform) }.unwrap();
- if conv::is_sampler(utype) {
+ if conv::is_opaque_type(utype) {
continue;
}
diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs
index 796379575d6..a6571e678cf 100644
--- a/wgpu-hal/src/vulkan/adapter.rs
+++ b/wgpu-hal/src/vulkan/adapter.rs
@@ -172,9 +172,7 @@ impl PhysicalDeviceFeatures {
//.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY))
.geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX))
.build(),
- descriptor_indexing: if enabled_extensions
- .contains(&vk::ExtDescriptorIndexingFn::name())
- {
+ descriptor_indexing: if requested_features.intersects(indexing_features()) {
Some(
vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::builder()
.shader_sampled_image_array_non_uniform_indexing(
@@ -229,7 +227,9 @@ impl PhysicalDeviceFeatures {
} else {
None
},
- image_robustness: if enabled_extensions.contains(&vk::ExtImageRobustnessFn::name()) {
+ image_robustness: if effective_api_version >= vk::API_VERSION_1_3
+ || enabled_extensions.contains(&vk::ExtImageRobustnessFn::name())
+ {
Some(
vk::PhysicalDeviceImageRobustnessFeaturesEXT::builder()
.robust_image_access(private_caps.robust_image_access)
@@ -412,7 +412,7 @@ impl PhysicalDeviceFeatures {
//if caps.supports_extension(vk::ExtSamplerFilterMinmaxFn::name()) {
features.set(
F::MULTI_DRAW_INDIRECT_COUNT,
- caps.supports_extension(khr::DrawIndirectCount::name()),
+ caps.supports_extension(vk::KhrDrawIndirectCountFn::name()),
);
features.set(
F::CONSERVATIVE_RASTERIZATION,
@@ -554,78 +554,118 @@ impl PhysicalDeviceCapabilities {
fn get_required_extensions(&self, requested_features: wgt::Features) -> Vec<&'static CStr> {
let mut extensions = Vec::new();
- extensions.push(khr::Swapchain::name());
+ // Note that quite a few extensions depend on the `VK_KHR_get_physical_device_properties2` instance extension.
+ // We enable `VK_KHR_get_physical_device_properties2` unconditionally (if available).
+
+ // Require `VK_KHR_swapchain`
+ extensions.push(vk::KhrSwapchainFn::name());
if self.effective_api_version < vk::API_VERSION_1_1 {
- extensions.push(vk::KhrMaintenance1Fn::name());
- extensions.push(vk::KhrMaintenance2Fn::name());
+ // Require either `VK_KHR_maintenance1` or `VK_AMD_negative_viewport_height`
+ if self.supports_extension(vk::KhrMaintenance1Fn::name()) {
+ extensions.push(vk::KhrMaintenance1Fn::name());
+ } else {
+ // `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside it
+ extensions.push(vk::AmdNegativeViewportHeightFn::name());
+ }
+
+ // Optional `VK_KHR_maintenance2`
+ if self.supports_extension(vk::KhrMaintenance2Fn::name()) {
+ extensions.push(vk::KhrMaintenance2Fn::name());
+ }
- // `VK_KHR_storage_buffer_storage_class` required for Naga on Vulkan 1.0 devices
+ // Require `VK_KHR_storage_buffer_storage_class`
extensions.push(vk::KhrStorageBufferStorageClassFn::name());
- // Below Vulkan 1.1 we can get multiview from an extension
+ // Require `VK_KHR_multiview` if the associated feature was requested
if requested_features.contains(wgt::Features::MULTIVIEW) {
extensions.push(vk::KhrMultiviewFn::name());
}
-
- // `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside `VK_KHR_maintenance1` or a 1.1+ device.
- if !self.supports_extension(vk::KhrMaintenance1Fn::name()) {
- extensions.push(vk::AmdNegativeViewportHeightFn::name());
- }
}
if self.effective_api_version < vk::API_VERSION_1_2 {
+ // Optional `VK_KHR_imageless_framebuffer`
if self.supports_extension(vk::KhrImagelessFramebufferFn::name()) {
extensions.push(vk::KhrImagelessFramebufferFn::name());
- extensions.push(vk::KhrImageFormatListFn::name()); // Required for `KhrImagelessFramebufferFn`
+ // Require `VK_KHR_image_format_list` due to it being a dependency
+ extensions.push(vk::KhrImageFormatListFn::name());
+ // Require `VK_KHR_maintenance2` due to it being a dependency
+ if self.effective_api_version < vk::API_VERSION_1_1 {
+ extensions.push(vk::KhrMaintenance2Fn::name());
+ }
}
- // This extension is core in Vulkan 1.2
+ // Optional `VK_KHR_driver_properties`
if self.supports_extension(vk::KhrDriverPropertiesFn::name()) {
extensions.push(vk::KhrDriverPropertiesFn::name());
}
- extensions.push(vk::ExtSamplerFilterMinmaxFn::name());
- extensions.push(vk::KhrTimelineSemaphoreFn::name());
+ // Optional `VK_KHR_timeline_semaphore`
+ if self.supports_extension(vk::KhrTimelineSemaphoreFn::name()) {
+ extensions.push(vk::KhrTimelineSemaphoreFn::name());
+ }
+ // Require `VK_EXT_descriptor_indexing` if one of the associated features was requested
if requested_features.intersects(indexing_features()) {
extensions.push(vk::ExtDescriptorIndexingFn::name());
+ // Require `VK_KHR_maintenance3` due to it being a dependency
if self.effective_api_version < vk::API_VERSION_1_1 {
extensions.push(vk::KhrMaintenance3Fn::name());
}
}
+ // Require `VK_KHR_shader_float16_int8` and `VK_KHR_16bit_storage` if the associated feature was requested
+ if requested_features.contains(wgt::Features::SHADER_FLOAT16) {
+ extensions.push(vk::KhrShaderFloat16Int8Fn::name());
+ // `VK_KHR_16bit_storage` requires `VK_KHR_storage_buffer_storage_class`, however we require that one already
+ if self.effective_api_version < vk::API_VERSION_1_1 {
+ extensions.push(vk::Khr16bitStorageFn::name());
+ }
+ }
+
//extensions.push(vk::KhrSamplerMirrorClampToEdgeFn::name());
//extensions.push(vk::ExtSamplerFilterMinmaxFn::name());
}
+ if self.effective_api_version < vk::API_VERSION_1_3 {
+ // Optional `VK_EXT_image_robustness`
+ if self.supports_extension(vk::ExtImageRobustnessFn::name()) {
+ extensions.push(vk::ExtImageRobustnessFn::name());
+ }
+ }
+
+ // Optional `VK_EXT_robustness2`
+ if self.supports_extension(vk::ExtRobustness2Fn::name()) {
+ extensions.push(vk::ExtRobustness2Fn::name());
+ }
+
+ // Require `VK_KHR_draw_indirect_count` if the associated feature was requested
// Even though Vulkan 1.2 has promoted the extension to core, we must require the extension to avoid
// large amounts of spaghetti involved with using PhysicalDeviceVulkan12Features.
if requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT) {
extensions.push(vk::KhrDrawIndirectCountFn::name());
}
+ // Require `VK_EXT_conservative_rasterization` if the associated feature was requested
if requested_features.contains(wgt::Features::CONSERVATIVE_RASTERIZATION) {
extensions.push(vk::ExtConservativeRasterizationFn::name());
}
+ // Require `VK_EXT_depth_clip_enable` if the associated feature was requested
if requested_features.contains(wgt::Features::DEPTH_CLIP_CONTROL) {
extensions.push(vk::ExtDepthClipEnableFn::name());
}
+ // Require `VK_KHR_portability_subset` on macOS/iOS
#[cfg(any(target_os = "macos", target_os = "ios"))]
extensions.push(vk::KhrPortabilitySubsetFn::name());
+ // Require `VK_EXT_texture_compression_astc_hdr` if the associated feature was requested
if requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
extensions.push(vk::ExtTextureCompressionAstcHdrFn::name());
}
- if requested_features.contains(wgt::Features::SHADER_FLOAT16) {
- extensions.push(vk::KhrShaderFloat16Int8Fn::name());
- extensions.push(vk::Khr16bitStorageFn::name());
- }
-
extensions
}
@@ -762,13 +802,12 @@ impl super::InstanceShared {
self.get_physical_device_properties
{
// Get these now to avoid borrowing conflicts later
- let supports_descriptor_indexing =
- capabilities.supports_extension(vk::ExtDescriptorIndexingFn::name());
- let supports_driver_properties = capabilities.properties.api_version
- >= vk::API_VERSION_1_2
+ let supports_descriptor_indexing = self.driver_api_version >= vk::API_VERSION_1_2
+ || capabilities.supports_extension(vk::ExtDescriptorIndexingFn::name());
+ let supports_driver_properties = self.driver_api_version >= vk::API_VERSION_1_2
|| capabilities.supports_extension(vk::KhrDriverPropertiesFn::name());
- let mut builder = vk::PhysicalDeviceProperties2::builder();
+ let mut builder = vk::PhysicalDeviceProperties2KHR::builder();
if supports_descriptor_indexing {
let next = capabilities
@@ -1411,10 +1450,10 @@ impl crate::Adapter for super::Adapter {
Tfc::SAMPLED_LINEAR,
features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR),
);
- flags.set(
- Tfc::SAMPLED_MINMAX,
- features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_MINMAX),
- );
+ // flags.set(
+ // Tfc::SAMPLED_MINMAX,
+ // features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_MINMAX),
+ // );
flags.set(
Tfc::STORAGE | Tfc::STORAGE_READ_WRITE,
features.contains(vk::FormatFeatureFlags::STORAGE_IMAGE),
diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs
index 0f828e3a265..bb0e3330982 100644
--- a/wgpu-hal/src/vulkan/instance.rs
+++ b/wgpu-hal/src/vulkan/instance.rs
@@ -153,6 +153,7 @@ impl super::Instance {
pub fn required_extensions(
entry: &ash::Entry,
+ _driver_api_version: u32,
flags: crate::InstanceFlags,
) -> Result, crate::InstanceError> {
let instance_extensions = entry
@@ -164,6 +165,8 @@ impl super::Instance {
// Check our extensions against the available extensions
let mut extensions: Vec<&'static CStr> = Vec::new();
+
+ // VK_KHR_surface
extensions.push(khr::Surface::name());
// Platform-specific WSI extensions
@@ -172,29 +175,40 @@ impl super::Instance {
not(target_os = "android"),
not(target_os = "macos")
)) {
+ // VK_KHR_xlib_surface
extensions.push(khr::XlibSurface::name());
+ // VK_KHR_xcb_surface
extensions.push(khr::XcbSurface::name());
+ // VK_KHR_wayland_surface
extensions.push(khr::WaylandSurface::name());
}
if cfg!(target_os = "android") {
+ // VK_KHR_android_surface
extensions.push(khr::AndroidSurface::name());
}
if cfg!(target_os = "windows") {
+ // VK_KHR_win32_surface
extensions.push(khr::Win32Surface::name());
}
if cfg!(target_os = "macos") {
+ // VK_EXT_metal_surface
extensions.push(ext::MetalSurface::name());
}
if flags.contains(crate::InstanceFlags::DEBUG) {
+ // VK_EXT_debug_utils
extensions.push(ext::DebugUtils::name());
}
- extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name());
-
+ // VK_EXT_swapchain_colorspace
// Provid wide color gamut
extensions.push(vk::ExtSwapchainColorspaceFn::name());
+ // VK_KHR_get_physical_device_properties2
+ // Even though the extension was promoted to Vulkan 1.1, we still require the extension
+ // so that we don't have to conditionally use the functions provided by the 1.1 instance
+ extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name());
+
// Only keep available extensions.
extensions.retain(|&ext| {
if instance_extensions.iter().any(|inst_ext| {
@@ -262,19 +276,16 @@ impl super::Instance {
None
};
- // We can't use any of Vulkan-1.1+ abilities on Vk 1.0 instance,
- // so disabling this query helps.
- let get_physical_device_properties = if driver_api_version >= vk::API_VERSION_1_1
- && extensions.contains(&khr::GetPhysicalDeviceProperties2::name())
- {
- log::info!("Enabling device properties2");
- Some(khr::GetPhysicalDeviceProperties2::new(
- &entry,
- &raw_instance,
- ))
- } else {
- None
- };
+ let get_physical_device_properties =
+ if extensions.contains(&khr::GetPhysicalDeviceProperties2::name()) {
+ log::info!("Enabling device properties2");
+ Some(khr::GetPhysicalDeviceProperties2::new(
+ &entry,
+ &raw_instance,
+ ))
+ } else {
+ None
+ };
Ok(Self {
shared: Arc::new(super::InstanceShared {
@@ -519,7 +530,7 @@ impl crate::Instance for super::Instance {
},
);
- let extensions = Self::required_extensions(&entry, desc.flags)?;
+ let extensions = Self::required_extensions(&entry, driver_api_version, desc.flags)?;
let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| {
log::info!("enumerate_instance_layer_properties: {:?}", e);
diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs
index 373be2d75e2..681256e938c 100644
--- a/wgpu-types/src/lib.rs
+++ b/wgpu-types/src/lib.rs
@@ -3847,6 +3847,7 @@ pub enum PresentMode {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "trace", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
+#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
pub enum CompositeAlphaMode {
/// Chooses either `Opaque` or `Inherit` automatically,depending on the
/// `alpha_mode` that the current surface can support.
diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml
index 4aec1f8ae60..c5a02e7fddb 100644
--- a/wgpu/Cargo.toml
+++ b/wgpu/Cargo.toml
@@ -92,7 +92,7 @@ replay = ["serde", "wgc/replay"]
angle = ["wgc/angle"]
webgl = ["hal", "wgc"]
emscripten = ["webgl"]
-vulkan-portability = ["wgc/vulkan-portability"]
+vulkan-portability = ["wgc/vulkan"]
expose-ids = []
# wgpu-core is always available as an optional dependency, "wgc".
@@ -122,7 +122,7 @@ workspace = true
features = ["dx11", "dx12"]
# We want the wgpu-core Vulkan backend on Unix (but not Emscripten) and Windows.
-[target.'cfg(any(windows, all(unix, not(target_arch = "emscripten"))))'.dependencies.wgc]
+[target.'cfg(any(windows, all(unix, not(target_arch = "emscripten"), not(target_os = "ios"), not(target_os = "macos"))))'.dependencies.wgc]
workspace = true
features = ["vulkan"]
diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs
index 3e4d5f11805..e11d0b9f935 100644
--- a/wgpu/src/lib.rs
+++ b/wgpu/src/lib.rs
@@ -197,6 +197,7 @@ pub struct Buffer {
map_context: Mutex,
size: wgt::BufferAddress,
usage: BufferUsages,
+ // Todo: missing map_state https://www.w3.org/TR/webgpu/#dom-gpubuffer-mapstate
}
static_assertions::assert_impl_all!(Buffer: Send, Sync);
@@ -227,6 +228,7 @@ pub struct Texture {
id: ObjectId,
data: Box,
owned: bool,
+ descriptor: TextureDescriptor<'static>,
}
static_assertions::assert_impl_all!(Texture: Send, Sync);
@@ -282,6 +284,13 @@ pub struct Surface {
context: Arc,
id: ObjectId,
data: Box,
+ // Stores the latest `SurfaceConfiguration` that was set using `Surface::configure`.
+ // It is required to set the attributes of the `SurfaceTexture` in the
+ // `Surface::get_current_texture` method.
+ // Because the `Surface::configure` method operates on an immutable reference this type has to
+ // be wrapped in a mutex and since the configuration is only supplied after the surface has
+ // been created is is additionally wrapped in an option.
+ config: Mutex