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>, } static_assertions::assert_impl_all!(Surface: Send, Sync); @@ -1448,6 +1457,7 @@ impl Instance { context: Arc::clone(&self.context), id, data, + config: Mutex::new(None), }) } @@ -1472,6 +1482,7 @@ impl Instance { context: Arc::clone(&self.context), id: ObjectId::from(surface.id()), data: Box::new(surface), + config: Mutex::new(None), } } @@ -1493,6 +1504,7 @@ impl Instance { context: Arc::clone(&self.context), id: ObjectId::from(surface.id()), data: Box::new(surface), + config: Mutex::new(None), } } @@ -1528,6 +1540,7 @@ impl Instance { id: ObjectId::from(surface), #[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] data: Box::new(()), + config: Mutex::new(None), }) } @@ -1563,6 +1576,7 @@ impl Instance { id: ObjectId::from(surface), #[cfg(all(target_arch = "wasm32", not(feature = "webgl")))] data: Box::new(()), + config: Mutex::new(None), }) } @@ -2038,6 +2052,10 @@ impl Device { id, data, owned: true, + descriptor: TextureDescriptor { + label: None, + ..desc.clone() + }, } } @@ -2070,6 +2088,10 @@ impl Device { id: ObjectId::from(texture.id()), data: Box::new(texture), owned: true, + descriptor: TextureDescriptor { + label: None, + ..desc.clone() + }, } } @@ -2386,7 +2408,7 @@ impl Buffer { /// Returns the length of the buffer allocation in bytes. /// /// This is always equal to the `size` that was specified when creating the buffer. - pub fn size(&self) -> wgt::BufferAddress { + pub fn size(&self) -> BufferAddress { self.size } @@ -2522,6 +2544,69 @@ impl Texture { aspect: TextureAspect::All, } } + + /// Returns the size of this `Texture`. + /// + /// This is always equal to the `size` that was specified when creating the texture. + pub fn size(&self) -> Extent3d { + self.descriptor.size + } + + /// Returns the width of this `Texture`. + /// + /// This is always equal to the `size.width` that was specified when creating the texture. + pub fn width(&self) -> u32 { + self.descriptor.size.width + } + + /// Returns the height of this `Texture`. + /// + /// This is always equal to the `size.height` that was specified when creating the texture. + pub fn height(&self) -> u32 { + self.descriptor.size.height + } + + /// Returns the depth or layer count of this `Texture`. + /// + /// This is always equal to the `size.depth_or_array_layers` that was specified when creating the texture. + pub fn depth_or_array_layers(&self) -> u32 { + self.descriptor.size.depth_or_array_layers + } + + /// Returns the mip_level_count of this `Texture`. + /// + /// This is always equal to the `mip_level_count` that was specified when creating the texture. + pub fn mip_level_count(&self) -> u32 { + self.descriptor.mip_level_count + } + + /// Returns the sample_count of this `Texture`. + /// + /// This is always equal to the `sample_count` that was specified when creating the texture. + pub fn sample_count(&self) -> u32 { + self.descriptor.sample_count + } + + /// Returns the dimension of this `Texture`. + /// + /// This is always equal to the `dimension` that was specified when creating the texture. + pub fn dimension(&self) -> TextureDimension { + self.descriptor.dimension + } + + /// Returns the format of this `Texture`. + /// + /// This is always equal to the `format` that was specified when creating the texture. + pub fn format(&self) -> TextureFormat { + self.descriptor.format + } + + /// Returns the allowed usages of this `Texture`. + /// + /// This is always equal to the `usage` that was specified when creating the texture. + pub fn usage(&self) -> TextureUsages { + self.descriptor.usage + } } impl Drop for Texture { @@ -3959,7 +4044,10 @@ impl Surface { &device.id, device.data.as_ref(), config, - ) + ); + + let mut conf = self.config.lock(); + *conf = Some(config.clone()); } /// Returns the next texture to be presented by the swapchain for drawing. @@ -3982,6 +4070,25 @@ impl Surface { SurfaceStatus::Lost => return Err(SurfaceError::Lost), }; + let guard = self.config.lock(); + let config = guard + .as_ref() + .expect("This surface has not been configured yet."); + + let descriptor = TextureDescriptor { + label: None, + size: Extent3d { + width: config.width, + height: config.height, + depth_or_array_layers: 1, + }, + format: config.format, + usage: config.usage, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + }; + texture_id .zip(texture_data) .map(|(id, data)| SurfaceTexture { @@ -3990,6 +4097,7 @@ impl Surface { id, data, owned: false, + descriptor, }, suboptimal, presented: false,