diff --git a/CHANGELOG.md b/CHANGELOG.md index 050f1c9cff..d11998c011 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,44 +52,44 @@ Bottom level categories: #### General - Many numeric built-ins have had a constant evaluation implementation added for them, which allows them to be used in a `const` context: - - [#4879](https://github.com/gfx-rs/wgpu/pull/4879) by @ErichDonGubler: - - `abs` - - `acos` - - `acosh` - - `asin` - - `asinh` - - `atan` - - `atanh` - - `cos` - - `cosh` - - `round` - - `saturate` - - `sin` - - `sinh` - - `sqrt` - - `step` - - `tan` - - `tanh` - - [#5098](https://github.com/gfx-rs/wgpu/pull/5098) by @ErichDonGubler: - - `ceil` - - `countLeadingZeros` - - `countOneBits` - - `countTrailingZeros` - - `degrees` - - `exp` - - `exp2` - - `floor` - - `fract` - - `fma` - - `inverseSqrt` - - `log` - - `log2` - - `max` - - `min` - - `radians` - - `reverseBits` - - `sign` - - `trunc` + - [#4879](https://github.com/gfx-rs/wgpu/pull/4879) by @ErichDonGubler: + - `abs` + - `acos` + - `acosh` + - `asin` + - `asinh` + - `atan` + - `atanh` + - `cos` + - `cosh` + - `round` + - `saturate` + - `sin` + - `sinh` + - `sqrt` + - `step` + - `tan` + - `tanh` + - [#5098](https://github.com/gfx-rs/wgpu/pull/5098) by @ErichDonGubler: + - `ceil` + - `countLeadingZeros` + - `countOneBits` + - `countTrailingZeros` + - `degrees` + - `exp` + - `exp2` + - `floor` + - `fract` + - `fma` + - `inverseSqrt` + - `log` + - `log2` + - `max` + - `min` + - `radians` + - `reverseBits` + - `sign` + - `trunc` - Eager release of GPU resources comes from device.trackers. By @bradwerth in [#5075](https://github.com/gfx-rs/wgpu/pull/5075) - `wgpu-types`'s `trace` and `replay` features have been replaced by the `serde` feature. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149) - `wgpu-core`'s `serial-pass` feature has been removed. Use `serde` instead. By @KirmesBude in [#5149](https://github.com/gfx-rs/wgpu/pull/5149) @@ -101,10 +101,12 @@ Bottom level categories: By @ErichDonGubler in [#5146](https://github.com/gfx-rs/wgpu/pull/5146), [#5046](https://github.com/gfx-rs/wgpu/pull/5046). - Signed and unsigned 64 bit integer support in shaders. By @rodolphito and @cwfitzgerald in [#5154](https://github.com/gfx-rs/wgpu/pull/5154) - `wgpu::Instance` can now report which `wgpu::Backends` are available based on the build configuration. By @wumpf [#5167](https://github.com/gfx-rs/wgpu/pull/5167) -```diff --wgpu::Instance::any_backend_feature_enabled() -+!wgpu::Instance::enabled_backend_features().is_empty() -``` + + ```diff + -wgpu::Instance::any_backend_feature_enabled() + +!wgpu::Instance::enabled_backend_features().is_empty() + ``` + - `wgpu::CommandEncoder::write_timestamp` requires now the new `wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS` feature which is available on all native backends but not on WebGPU (due to a spec change `write_timestamp` is no longer supported on WebGPU). By @wumpf in [#5188](https://github.com/gfx-rs/wgpu/pull/5188) - Breaking change: [`wgpu_core::pipeline::ProgrammableStageDescriptor`](https://docs.rs/wgpu-core/latest/wgpu_core/pipeline/struct.ProgrammableStageDescriptor.html#structfield.entry_point) is now optional. By @ErichDonGubler in [#5305](https://github.com/gfx-rs/wgpu/pull/5305). - `Features::downlevel{_webgl2,}_features` was made const by @MultisampledNight in [#5343](https://github.com/gfx-rs/wgpu/pull/5343) @@ -162,11 +164,20 @@ Bottom level categories: - Fixes for being able to use an OpenGL 4.1 core context provided by macOS with wgpu. By @bes in [#5331](https://github.com/gfx-rs/wgpu/pull/5331). - Don't create a program for shader-clearing if that workaround isn't required. By @Dinnerbone in [#5348](https://github.com/gfx-rs/wgpu/pull/5348). - Fix crash when holding multiple devices on wayland/surfaceless. By @ashdnazg in [#5351](https://github.com/gfx-rs/wgpu/pull/5351). +- Don't depend on bind group and bind group layout entry order in HAL. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421). #### Vulkan - Set object labels when the DEBUG flag is set, even if the VALIDATION flag is disabled. By @DJMcNab in [#5345](https://github.com/gfx-rs/wgpu/pull/5345). +#### Metal + +- Don't depend on bind group and bind group layout entry order in HAL. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421). + +#### DX12 + +- Don't depend on bind group and bind group layout entry order in HAL. This caused incorrect severely incorrect command execution and, in some cases, crashes. By @ErichDonGubler in [#5421](https://github.com/gfx-rs/wgpu/pull/5421). + ## v0.19.3 (2024-03-01) This release includes `wgpu`, `wgpu-core`, and `wgpu-hal`. All other crates are unchanged. @@ -188,6 +199,7 @@ By @cwfitzgerald in [#5325](https://github.com/gfx-rs/wgpu/pull/5325). #### General - Fix an issue where command encoders weren't properly freed if an error occurred during command encoding. By @ErichDonGubler in [#5251](https://github.com/gfx-rs/wgpu/pull/5251). +- Fix incorrect validation causing all indexed draws on render bundles to fail. By @wumpf in [#5430](https://github.com/gfx-rs/wgpu/pull/5340). #### Android - Fix linking error when targeting android without `winit`. By @ashdnazg in [#5326](https://github.com/gfx-rs/wgpu/pull/5326). diff --git a/Cargo.lock b/Cargo.lock index e1aaddcfcf..eb2e05042f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,6 +708,12 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6245d59a3e82a7fc217c5828a6692dbc6dfb63a0c8c90495621f7b9d79704a0e" +[[package]] +name = "cooked-waker" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147be55d677052dabc6b22252d5dd0fd4c29c8c27aa4f2fbef0f94aa003b406f" + [[package]] name = "core-foundation" version = "0.9.4" @@ -970,26 +976,32 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.125.0" +version = "0.143.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92543d4f4d82f2350123bd4b60e97a73aba1a9bbca8c931e827459096dedabba" +checksum = "1f770d8deb0eb0bfd596d242d9eaef5312ef57f0130964cb53c7f6a8107d13be" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.232.0" +version = "0.272.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "229ffd108e028b148a1a5a6122f771bc7c37094170226f44b8b93b3a9b79d114" +checksum = "07093891f2af763023614cfe2d1ce5f9ce5a7920c4fcf2f00911bd0d93083523" dependencies = [ "anyhow", + "bincode", + "bit-set", + "bit-vec", "bytes", + "cooked-waker", + "deno_core_icudata", "deno_ops", "deno_unsync", "futures", "libc", "log", + "memoffset 0.9.1", "parking_lot", "pin-project", "serde", @@ -997,16 +1009,23 @@ dependencies = [ "serde_v8", "smallvec", "sourcemap", + "static_assertions", "tokio", "url", "v8", ] +[[package]] +name = "deno_core_icudata" +version = "0.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13951ea98c0a4c372f162d669193b4c9d991512de9f2381dd161027f34b26b1" + [[package]] name = "deno_ops" -version = "0.108.0" +version = "0.148.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7dde627916f8539f3f0d2e754dda40810c8ca4d655f2eaac1ef54785a12fd27" +checksum = "5bc73fc07ad26e71715d5a726d1dd228587c0d121a591b1931a0fcf958a2ec3b" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -1028,9 +1047,9 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.125.0" +version = "0.143.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25ec92af225230fe4a429de0b5891f35b1ba5f143f8c1605bb7b9d1cb767ac73" +checksum = "39d9e6ffd6a7157bfd3cf1385c59232e49587c9bbb898e64010f7f082242a203" dependencies = [ "deno_core", "serde", @@ -1039,9 +1058,9 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.156.0" +version = "0.174.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3aeef7522f46b3442e24a750ef914ca54aade2110d6540a66e4ea17b4eb68bb7" +checksum = "708666b5b346e6880c1372006615814db7fc5ef36bd1785f0b0e4f8617082999" dependencies = [ "async-trait", "base64-simd 0.8.0", @@ -1071,9 +1090,9 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.125.0" +version = "0.143.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74b1a86e9a1dec0dc5d4dc132faee72ac50297f41e30f7cab57dd52dda380eed" +checksum = "bddad93aa68e3c3c2d36976cd401af27a6ce750c23060e02401daf240f2acbe2" dependencies = [ "deno_core", ] @@ -1381,9 +1400,9 @@ dependencies = [ [[package]] name = "fslock" -version = "0.1.8" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57eafdd0c16f57161105ae1b98a1238f97645f2f588438b2949c99a2af9616bf" +checksum = "04412b8935272e3a9bae6f48c7bfff74c2911f60525404edfdd28e49884c3bfb" dependencies = [ "libc", "winapi", @@ -1675,6 +1694,15 @@ dependencies = [ "bitflags 2.4.2", ] +[[package]] +name = "gzip-header" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" +dependencies = [ + "crc32fast", +] + [[package]] name = "half" version = "2.4.0" @@ -2048,6 +2076,15 @@ dependencies = [ "autocfg", ] +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + [[package]] name = "metal" version = "0.27.0" @@ -2251,7 +2288,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -2264,7 +2301,7 @@ dependencies = [ "bitflags 1.3.2", "cfg-if", "libc", - "memoffset", + "memoffset 0.6.5", ] [[package]] @@ -3057,12 +3094,11 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.141.0" +version = "0.181.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc689cb316d67b200e9f7449ce76cceb7e483e0f828d1a9c3d057c4367b6c26e" +checksum = "fd25bb66a20a1a405fb3733aaaf8a8a77a14fd55c8f5fd9db2a2e95bbd7eeab9" dependencies = [ "bytes", - "derive_more", "num-bigint", "serde", "smallvec", @@ -3656,12 +3692,15 @@ dependencies = [ [[package]] name = "v8" -version = "0.81.0" +version = "0.89.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b75f5f378b9b54aff3b10da8170d26af4cfd217f644cf671badcd13af5db4beb" +checksum = "fe2197fbef82c98f7953d13568a961d4e1c663793b5caf3c74455a13918cdf33" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.4.2", "fslock", + "gzip-header", + "home", + "miniz_oxide", "once_cell", "which", ] @@ -4209,14 +4248,15 @@ dependencies = [ [[package]] name = "which" -version = "4.4.2" +version = "5.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +checksum = "9bf3ea8596f3a0dd5980b46430f2058dfe2c36a27ccfbb1845d6fbfcd9ba6e14" dependencies = [ "either", "home", "once_cell", "rustix", + "windows-sys 0.48.0", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a059d7b86e..7cb26434bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -165,11 +165,11 @@ web-sys = "0.3.69" web-time = "0.2.4" # deno dependencies -deno_console = "0.125.0" -deno_core = "0.232.0" -deno_url = "0.125.0" -deno_web = "0.156.0" -deno_webidl = "0.125.0" +deno_console = "0.143.0" +deno_core = "0.272.0" +deno_url = "0.143.0" +deno_web = "0.174.0" +deno_webidl = "0.143.0" deno_webgpu = { version = "0.85.0", path = "./deno_webgpu" } tokio = "1.36.0" termcolor = "1.4.1" diff --git a/cts_runner/src/bootstrap.js b/cts_runner/src/bootstrap.js index 640075e7ee..b168843931 100644 --- a/cts_runner/src/bootstrap.js +++ b/cts_runner/src/bootstrap.js @@ -5,8 +5,7 @@ // delete Object.prototype.__proto__; -const core = Deno.core; -const primordials = globalThis.__bootstrap.primordials; +import { core, primordials } from "ext:core/mod.js"; const { Error, ObjectDefineProperty, @@ -25,9 +24,10 @@ import * as base64 from "ext:deno_web/05_base64.js"; import * as encoding from "ext:deno_web/08_text_encoding.js"; import { Console } from "ext:deno_console/01_console.js"; import * as url from "ext:deno_url/00_url.js"; -import DOMException from "ext:deno_web/01_dom_exception.js"; +import { DOMException } from "ext:deno_web/01_dom_exception.js"; import * as performance from "ext:deno_web/15_performance.js"; import * as webgpu from "ext:deno_webgpu/01_webgpu.js"; +import * as imageData from "ext:deno_web/16_image_data.js"; // imports needed to pass module evaluation import "ext:deno_url/01_urlpattern.js"; @@ -42,8 +42,6 @@ import "ext:deno_web/14_compression.js"; let globalThis_; -const { BadResource, Interrupted } = core; - class NotFound extends Error { constructor(msg) { super(msg); @@ -183,6 +181,7 @@ const windowOrWorkerGlobalScope = { clearInterval: util.writable(timers.clearInterval), clearTimeout: util.writable(timers.clearTimeout), performance: util.writable(performance.performance), + ImageData: core.propNonEnumerable(imageData.ImageData), GPU: util.nonEnumerable(webgpu.GPU), GPUAdapter: util.nonEnumerable(webgpu.GPUAdapter), @@ -248,10 +247,8 @@ core.registerErrorClass("NotFound", NotFound); core.registerErrorClass("AlreadyExists", AlreadyExists); core.registerErrorClass("InvalidData", InvalidData); core.registerErrorClass("TimedOut", TimedOut); -core.registerErrorClass("Interrupted", Interrupted); core.registerErrorClass("WriteZero", WriteZero); core.registerErrorClass("UnexpectedEof", UnexpectedEof); -core.registerErrorClass("BadResource", BadResource); core.registerErrorClass("NotSupported", NotSupported); core.registerErrorBuilder( "DOMExceptionOperationError", diff --git a/cts_runner/src/main.rs b/cts_runner/src/main.rs index fa1b8baa11..201fda80d7 100644 --- a/cts_runner/src/main.rs +++ b/cts_runner/src/main.rs @@ -45,13 +45,13 @@ mod native { ], ..Default::default() }; - let mut isolate = JsRuntime::new(options); + let mut js_runtime = JsRuntime::new(options); let args = args_iter.collect::>(); let cfg = json!({"args": args, "cwd": env::current_dir().unwrap().to_string_lossy() }); { - let context = isolate.main_context(); - let scope = &mut isolate.handle_scope(); + let context = js_runtime.main_context(); + let scope = &mut js_runtime.handle_scope(); let context_local = v8::Local::new(scope, context); let global_obj = context_local.global(scope); let bootstrap_str = v8::String::new(scope, "bootstrap").unwrap(); @@ -65,20 +65,12 @@ mod native { .unwrap(); } - isolate.op_state().borrow_mut().put(Permissions {}); + js_runtime.op_state().borrow_mut().put(Permissions {}); - let mod_id = isolate.load_main_module(&specifier, None).await?; - let mod_rx = isolate.mod_evaluate(mod_id); - - let rx = tokio::spawn(async move { - match mod_rx.await { - Ok(err @ Err(_)) => err, - _ => Ok(()), - } - }); - - isolate.run_event_loop(false).await?; - rx.await.unwrap()?; + let mod_id = js_runtime.load_main_es_module(&specifier).await?; + let result = js_runtime.mod_evaluate(mod_id); + js_runtime.run_event_loop(Default::default()).await?; + result.await?; Ok(()) } @@ -87,7 +79,7 @@ mod native { cts_runner, deps = [deno_webidl, deno_web], ops = [op_exit, op_read_file_sync, op_write_file_sync], - esm_entry_point = "ext:cts_runner/bootstrap.js", + esm_entry_point = "ext:cts_runner/src/bootstrap.js", esm = ["src/bootstrap.js"], ); diff --git a/deno_webgpu/01_webgpu.js b/deno_webgpu/01_webgpu.js index d7a88ad445..6aecb03231 100644 --- a/deno_webgpu/01_webgpu.js +++ b/deno_webgpu/01_webgpu.js @@ -6,12 +6,11 @@ /// /// -const core = globalThis.Deno.core; -const ops = core.ops; -const primordials = globalThis.__bootstrap.primordials; +import { core, primordials } from "ext:core/mod.js"; +import * as ops from "ext:core/ops"; import * as webidl from "ext:deno_webidl/00_webidl.js"; import { EventTarget } from "ext:deno_web/02_event.js"; -import DOMException from "ext:deno_web/01_dom_exception.js"; +import { DOMException } from "ext:deno_web/01_dom_exception.js"; import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; const { ArrayBuffer, @@ -247,8 +246,7 @@ class GPU { "Argument 1", ); - const { err, ...data } = await core.opAsync( - "op_webgpu_request_adapter", + const { err, ...data } = await ops.op_webgpu_request_adapter( options.powerPreference, options.forceFallbackAdapter, ); @@ -338,8 +336,7 @@ class GPUAdapter { } } - const { rid, features, limits } = await core.opAsync( - "op_webgpu_request_device", + const { rid, features, limits } = await ops.op_webgpu_request_device( this[_adapter].rid, descriptor.label, requiredFeatures, @@ -377,10 +374,7 @@ class GPUAdapter { architecture, device, description, - } = await core.opAsync( - "op_webgpu_request_adapter_info", - this[_adapter].rid, - ); + } = await ops.op_webgpu_request_adapter_info(this[_adapter].rid); const adapterInfo = webidl.createBranded(GPUAdapterInfo); adapterInfo[_vendor] = unmaskHints.includes("vendor") ? vendor : ""; @@ -677,6 +671,7 @@ class GPUSupportedFeatures { constructor() { webidl.illegalConstructor(); } + [SymbolFor("Deno.privateCustomInspect")](inspect, inspectOptions) { if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) { return `${this.constructor.name} ${ @@ -687,23 +682,23 @@ class GPUSupportedFeatures { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; } } +} +const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype; - const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype; - - /** - * @param {string | undefined} reason - * @param {string} message - * @returns {GPUDeviceLostInfo} - */ - function createGPUDeviceLostInfo(reason, message) { - /** @type {GPUDeviceLostInfo} */ - const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo); - deviceLostInfo[_reason] = reason ?? "unknown"; - deviceLostInfo[_message] = message; - return deviceLostInfo; - } +/** + * @param {string | undefined} reason + * @param {string} message + * @returns {GPUDeviceLostInfo} + */ +function createGPUDeviceLostInfo(reason, message) { + /** @type {GPUDeviceLostInfo} */ + const deviceLostInfo = webidl.createBranded(GPUDeviceLostInfo); + deviceLostInfo[_reason] = reason ?? "unknown"; + deviceLostInfo[_message] = message; + return deviceLostInfo; +} - class GPUDeviceLostInfo { +class GPUDeviceLostInfo { /** @type {string} */ [_reason]; /** @type {string} */ diff --git a/tests/tests/device.rs b/tests/tests/device.rs index 621ac74f6f..fd393b63bf 100644 --- a/tests/tests/device.rs +++ b/tests/tests/device.rs @@ -654,3 +654,125 @@ static DROPPED_GLOBAL_THEN_DEVICE_LOST: GpuTestConfiguration = GpuTestConfigurat "Device lost callback should have been called." ); }); + +#[gpu_test] +static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default()) + .run_sync(|ctx| { + // This test addresses a bug found in multiple backends where `wgpu_core` and `wgpu_hal` + // backends made different assumptions about the element order of vectors of bind group + // layout entries and bind group resource bindings. + // + // Said bug was exposed originally by: + // + // 1. Shader-declared bindings having a different order than resource bindings provided to + // `Device::create_bind_group`. + // 2. Having more of one type of resource in the bind group than another. + // + // …such that internals would accidentally attempt to use an out-of-bounds index (of one + // resource type) in the wrong list of a different resource type. Let's reproduce that + // here. + + let trivial_shaders_with_some_reversed_bindings = "\ +@group(0) @binding(3) var myTexture2: texture_2d; +@group(0) @binding(2) var myTexture1: texture_2d; +@group(0) @binding(1) var mySampler: sampler; + +@fragment +fn fs_main(@builtin(position) pos: vec4) -> @location(0) vec4f { + return textureSample(myTexture1, mySampler, pos.xy) + textureSample(myTexture2, mySampler, pos.xy); +} + +@vertex +fn vs_main() -> @builtin(position) vec4 { + return vec4(0.0, 0.0, 0.0, 1.0); +} +"; + + let trivial_shaders_with_some_reversed_bindings = + ctx.device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl( + trivial_shaders_with_some_reversed_bindings.into(), + ), + }); + + let my_texture = ctx.device.create_texture(&wgt::TextureDescriptor { + label: None, + size: wgt::Extent3d { + width: 1024, + height: 512, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgt::TextureDimension::D2, + format: wgt::TextureFormat::Rgba8Unorm, + usage: wgt::TextureUsages::RENDER_ATTACHMENT | wgt::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + + let my_texture_view = my_texture.create_view(&wgpu::TextureViewDescriptor { + label: None, + format: None, + dimension: None, + aspect: wgt::TextureAspect::All, + base_mip_level: 0, + mip_level_count: None, + base_array_layer: 0, + array_layer_count: None, + }); + + let my_sampler = ctx + .device + .create_sampler(&wgpu::SamplerDescriptor::default()); + + let render_pipeline = ctx + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + fragment: Some(wgpu::FragmentState { + module: &trivial_shaders_with_some_reversed_bindings, + entry_point: "fs_main", + targets: &[Some(wgt::ColorTargetState { + format: wgt::TextureFormat::Bgra8Unorm, + blend: None, + write_mask: wgt::ColorWrites::ALL, + })], + }), + layout: None, + + // Other fields below aren't interesting for this text. + label: None, + vertex: wgpu::VertexState { + module: &trivial_shaders_with_some_reversed_bindings, + entry_point: "vs_main", + buffers: &[], + }, + primitive: wgt::PrimitiveState::default(), + depth_stencil: None, + multisample: wgt::MultisampleState::default(), + multiview: None, + }); + + // fail(&ctx.device, || { + // }, ""); + ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &render_pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&my_sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(&my_texture_view), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: wgpu::BindingResource::TextureView(&my_texture_view), + }, + ], + }); + }); diff --git a/tests/tests/vertex_indices/mod.rs b/tests/tests/vertex_indices/mod.rs index 745eeff8c3..e0a2cbae06 100644 --- a/tests/tests/vertex_indices/mod.rs +++ b/tests/tests/vertex_indices/mod.rs @@ -5,9 +5,10 @@ use std::{num::NonZeroU64, ops::Range}; -use wgpu::util::{BufferInitDescriptor, DeviceExt}; +use wgpu::util::{BufferInitDescriptor, DeviceExt, RenderEncoder}; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgt::RenderBundleDescriptor; /// Generic struct representing a draw call struct Draw { @@ -19,7 +20,7 @@ struct Draw { impl Draw { /// Directly execute the draw call - fn execute(&self, rpass: &mut wgpu::RenderPass<'_>) { + fn execute(&self, rpass: &mut dyn RenderEncoder<'_>) { if let Some(base_vertex) = self.base_vertex { rpass.draw_indexed(self.vertex.clone(), base_vertex, self.instance.clone()); } else { @@ -64,7 +65,7 @@ impl Draw { /// Execute the draw call from the given indirect buffer fn execute_indirect<'rpass>( &self, - rpass: &mut wgpu::RenderPass<'rpass>, + rpass: &mut dyn RenderEncoder<'rpass>, indirect: &'rpass wgpu::Buffer, offset: &mut u64, ) { @@ -169,10 +170,21 @@ impl DrawCallKind { const ARRAY: [Self; 2] = [Self::Direct, Self::Indirect]; } +#[derive(Debug, Copy, Clone)] +enum EncoderKind { + RenderPass, + RenderBundle, +} + +impl EncoderKind { + const ARRAY: [Self; 2] = [Self::RenderPass, Self::RenderBundle]; +} + struct Test { case: TestCase, id_source: IdSource, draw_call_kind: DrawCallKind, + encoder_kind: EncoderKind, } impl Test { @@ -325,11 +337,14 @@ async fn vertex_index_common(ctx: TestingContext) { for case in TestCase::ARRAY { for id_source in IdSource::ARRAY { for draw_call_kind in DrawCallKind::ARRAY { - tests.push(Test { - case, - id_source, - draw_call_kind, - }) + for encoder_kind in EncoderKind::ARRAY { + tests.push(Test { + case, + id_source, + draw_call_kind, + encoder_kind, + }) + } } } } @@ -373,6 +388,7 @@ async fn vertex_index_common(ctx: TestingContext) { .device .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let render_bundle; let indirect_buffer; let mut rpass = encoder1.begin_render_pass(&wgpu::RenderPassDescriptor { label: None, @@ -386,34 +402,64 @@ async fn vertex_index_common(ctx: TestingContext) { occlusion_query_set: None, }); - rpass.set_vertex_buffer(0, identity_buffer.slice(..)); - rpass.set_vertex_buffer(1, identity_buffer.slice(..)); - rpass.set_index_buffer(identity_buffer.slice(..), wgpu::IndexFormat::Uint32); - rpass.set_pipeline(pipeline); - rpass.set_bind_group(0, &bg, &[]); - - let draws = test.case.draws(); - - match test.draw_call_kind { - DrawCallKind::Direct => { - for draw in draws { - draw.execute(&mut rpass); + { + // Need to scope render_bundle_encoder since it's not Send and would otherwise + // infect the function if not going out of scope before an await call. + // (it is dropped via `take` + `finish` earlier, but compiler does not take this into account) + let mut render_bundle_encoder = match test.encoder_kind { + EncoderKind::RenderPass => None, + EncoderKind::RenderBundle => Some(ctx.device.create_render_bundle_encoder( + &wgpu::RenderBundleEncoderDescriptor { + label: Some("test renderbundle encoder"), + color_formats: &[Some(wgpu::TextureFormat::Rgba8Unorm)], + depth_stencil: None, + sample_count: 1, + multiview: None, + }, + )), + }; + + let render_encoder: &mut dyn RenderEncoder = render_bundle_encoder + .as_mut() + .map(|r| r as &mut dyn RenderEncoder) + .unwrap_or(&mut rpass); + + render_encoder.set_vertex_buffer(0, identity_buffer.slice(..)); + render_encoder.set_vertex_buffer(1, identity_buffer.slice(..)); + render_encoder.set_index_buffer(identity_buffer.slice(..), wgpu::IndexFormat::Uint32); + render_encoder.set_pipeline(pipeline); + render_encoder.set_bind_group(0, &bg, &[]); + + let draws = test.case.draws(); + + match test.draw_call_kind { + DrawCallKind::Direct => { + for draw in draws { + draw.execute(render_encoder); + } } - } - DrawCallKind::Indirect => { - let mut indirect_bytes = Vec::new(); - for draw in draws { - draw.add_to_buffer(&mut indirect_bytes, features); + DrawCallKind::Indirect => { + let mut indirect_bytes = Vec::new(); + for draw in draws { + draw.add_to_buffer(&mut indirect_bytes, features); + } + indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor { + label: Some("indirect"), + contents: &indirect_bytes, + usage: wgpu::BufferUsages::INDIRECT, + }); + let mut offset = 0; + for draw in draws { + draw.execute_indirect(render_encoder, &indirect_buffer, &mut offset); + } } - indirect_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor { - label: Some("indirect"), - contents: &indirect_bytes, - usage: wgpu::BufferUsages::INDIRECT, + } + + if let Some(render_bundle_encoder) = render_bundle_encoder.take() { + render_bundle = render_bundle_encoder.finish(&RenderBundleDescriptor { + label: Some("test renderbundle"), }); - let mut offset = 0; - for draw in draws { - draw.execute_indirect(&mut rpass, &indirect_buffer, &mut offset); - } + rpass.execute_bundles([&render_bundle]); } } @@ -439,21 +485,18 @@ async fn vertex_index_common(ctx: TestingContext) { .panic_on_timeout(); let data: Vec = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec(); + let case_name = format!( + "Case {:?} getting indices from {:?} using {:?} draw calls, encoded with a {:?}", + test.case, test.id_source, test.draw_call_kind, test.encoder_kind + ); if data != expected { eprintln!( - "Failed: Got: {:?} Expected: {:?} - Case {:?} getting indices from {:?} using {:?} draw calls", - data, - expected, - test.case, - test.id_source, - test.draw_call_kind + "Failed: Got: {:?} Expected: {:?} - {case_name}", + data, expected, ); failed = true; } else { - eprintln!( - "Passed: Case {:?} getting indices from {:?} using {:?} draw calls", - test.case, test.id_source, test.draw_call_kind - ); + eprintln!("Passed: {case_name}"); } } diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index e2848f737d..47beda8ec6 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -166,7 +166,7 @@ fn validate_indexed_draw( ) -> Result<(), DrawError> { let last_index = first_index as u64 + index_count as u64; let index_limit = index_state.limit(); - if last_index <= index_limit { + if last_index > index_limit { return Err(DrawError::IndexBeyondLimit { last_index, index_limit, diff --git a/wgpu-hal/src/dx12/device.rs b/wgpu-hal/src/dx12/device.rs index 994126c265..23bd409dc4 100644 --- a/wgpu-hal/src/dx12/device.rs +++ b/wgpu-hal/src/dx12/device.rs @@ -1100,7 +1100,16 @@ impl crate::Device for super::Device { } let mut dynamic_buffers = Vec::new(); - for (layout, entry) in desc.layout.entries.iter().zip(desc.entries.iter()) { + let layout_and_entry_iter = desc.entries.iter().map(|entry| { + let layout = desc + .layout + .entries + .iter() + .find(|layout_entry| layout_entry.binding == entry.binding) + .expect("internal error: no layout entry found with binding slot"); + (layout, entry) + }); + for (layout, entry) in layout_and_entry_iter { match layout.ty { wgt::BindingType::Buffer { has_dynamic_offset: true, diff --git a/wgpu-hal/src/gles/device.rs b/wgpu-hal/src/gles/device.rs index 8cc3129d65..50c07f3ff0 100644 --- a/wgpu-hal/src/gles/device.rs +++ b/wgpu-hal/src/gles/device.rs @@ -1125,8 +1125,10 @@ impl crate::Device for super::Device { !0; bg_layout .entries - .last() - .map_or(0, |b| b.binding as usize + 1) + .iter() + .map(|b| b.binding) + .max() + .map_or(0, |idx| idx as usize + 1) ] .into_boxed_slice(); @@ -1179,7 +1181,16 @@ impl crate::Device for super::Device { ) -> Result { let mut contents = Vec::new(); - for (entry, layout) in desc.entries.iter().zip(desc.layout.entries.iter()) { + let layout_and_entry_iter = desc.entries.iter().map(|entry| { + let layout = desc + .layout + .entries + .iter() + .find(|layout_entry| layout_entry.binding == entry.binding) + .expect("internal error: no layout entry found with binding slot"); + (entry, layout) + }); + for (entry, layout) in layout_and_entry_iter { let binding = match layout.ty { wgt::BindingType::Buffer { .. } => { let bb = &desc.buffers[entry.resource_index as usize]; diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs index 8b8a9bb6e2..179429f5d7 100644 --- a/wgpu-hal/src/metal/device.rs +++ b/wgpu-hal/src/metal/device.rs @@ -708,7 +708,16 @@ impl crate::Device for super::Device { for (&stage, counter) in super::NAGA_STAGES.iter().zip(bg.counters.iter_mut()) { let stage_bit = map_naga_stage(stage); let mut dynamic_offsets_count = 0u32; - for (entry, layout) in desc.entries.iter().zip(desc.layout.entries.iter()) { + let layout_and_entry_iter = desc.entries.iter().map(|entry| { + let layout = desc + .layout + .entries + .iter() + .find(|layout_entry| layout_entry.binding == entry.binding) + .expect("internal error: no layout entry found with binding slot"); + (entry, layout) + }); + for (entry, layout) in layout_and_entry_iter { let size = layout.count.map_or(1, |c| c.get()); if let wgt::BindingType::Buffer { has_dynamic_offset: true, diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs index cf7c706d65..4a2c328fe7 100644 --- a/wgpu-hal/src/vulkan/adapter.rs +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -20,25 +20,85 @@ fn indexing_features() -> wgt::Features { | wgt::Features::PARTIALLY_BOUND_BINDING_ARRAY } -/// Aggregate of the `vk::PhysicalDevice*Features` structs used by `gfx`. +/// Features supported by a [`vk::PhysicalDevice`] and its extensions. +/// +/// This is used in two phases: +/// +/// - When enumerating adapters, this represents the features offered by the +/// adapter. [`Instance::expose_adapter`] calls `vkGetPhysicalDeviceFeatures2` +/// (or `vkGetPhysicalDeviceFeatures` if that is not available) to collect +/// this information about the `VkPhysicalDevice` represented by the +/// `wgpu_hal::ExposedAdapter`. +/// +/// - When opening a device, this represents the features we would like to +/// enable. At `wgpu_hal::Device` construction time, +/// [`PhysicalDeviceFeatures::from_extensions_and_requested_features`] +/// constructs an value of this type indicating which Vulkan features to +/// enable, based on the `wgpu_types::Features` requested. #[derive(Debug, Default)] pub struct PhysicalDeviceFeatures { + /// Basic Vulkan 1.0 features. core: vk::PhysicalDeviceFeatures, + + /// Features provided by `VK_EXT_descriptor_indexing`, promoted to Vulkan 1.2. pub(super) descriptor_indexing: Option, + + /// Features provided by `VK_KHR_imageless_framebuffer`, promoted to Vulkan 1.2. imageless_framebuffer: Option, + + /// Features provided by `VK_KHR_timeline_semaphore`, promoted to Vulkan 1.2 timeline_semaphore: Option, + + /// Features provided by `VK_EXT_image_robustness`, promoted to Vulkan 1.3 image_robustness: Option, + + /// Features provided by `VK_EXT_robustness2`. robustness2: Option, + + /// Features provided by `VK_KHR_multiview`, promoted to Vulkan 1.1. multiview: Option, + + /// Features provided by `VK_KHR_sampler_ycbcr_conversion`, promoted to Vulkan 1.1. sampler_ycbcr_conversion: Option, + + /// Features provided by `VK_EXT_texture_compression_astc_hdr`, promoted to Vulkan 1.3. astc_hdr: Option, + + /// Features provided by `VK_KHR_shader_float16_int8` (promoted to Vulkan + /// 1.2) and `VK_KHR_16bit_storage` (promoted to Vulkan 1.1). We use these + /// features together, or not at all. shader_float16: Option<( vk::PhysicalDeviceShaderFloat16Int8Features, vk::PhysicalDevice16BitStorageFeatures, )>, + + /// Features provided by `VK_KHR_acceleration_structure`. acceleration_structure: Option, + + /// Features provided by `VK_KHR_buffer_device_address`, promoted to Vulkan 1.2. + /// + /// We only use this feature for + /// [`Features::RAY_TRACING_ACCELERATION_STRUCTURE`], which requires + /// `VK_KHR_acceleration_structure`, which depends on + /// `VK_KHR_buffer_device_address`, so [`Instance::expose_adapter`] only + /// bothers to check if `VK_KHR_acceleration_structure` is available, + /// leaving this `None`. + /// + /// However, we do populate this when creating a device if + /// [`Features::RAY_TRACING_ACCELERATION_STRUCTURE`] is requested. buffer_device_address: Option, + + /// Features provided by `VK_KHR_ray_query`, + /// + /// Vulkan requires that the feature be present if the `VK_KHR_ray_query` + /// extension is present, so [`Instance::expose_adapter`] doesn't bother retrieving + /// this from `vkGetPhysicalDeviceFeatures2`. + /// + /// However, we do populate this when creating a device if ray tracing is requested. ray_query: Option, + + /// Features provided by `VK_KHR_zero_initialize_workgroup_memory`, promoted + /// to Vulkan 1.3. zero_initialize_workgroup_memory: Option, } @@ -639,15 +699,50 @@ impl PhysicalDeviceFeatures { } } -/// Information gathered about a physical device capabilities. +/// Information gathered about a physical device. +/// +/// This structure holds the results from the queries we make about a +/// [`vk::PhysicalDevice`], other than features: its device properties, +/// supported extensions, and whatever properties those extensions provide. +/// +/// Generally, if you get it from any of these functions, it's stored +/// here: +/// - `vkEnumerateDeviceExtensionProperties` +/// - `vkGetPhysicalDeviceProperties` +/// - `vkGetPhysicalDeviceProperties2` +/// +/// This also includes a copy of the device API version, since we can +/// use that as a shortcut for searching for an extension, if the +/// extension has been promoted to core in the current version. +/// +/// This does not include device features; for those, see +/// [`PhysicalDeviceFeatures`]. #[derive(Default, Debug)] pub struct PhysicalDeviceCapabilities { + /// Extensions supported by the `vk::PhysicalDevice`, + /// as returned by `vkEnumerateDeviceExtensionProperties`. supported_extensions: Vec, + + /// Properties of the `vk::PhysicalDevice`, as returned by + /// `vkGetPhysicalDeviceProperties`. properties: vk::PhysicalDeviceProperties, + + /// Additional `vk::PhysicalDevice` properties from the + /// `VK_KHR_maintenance3` extension, promoted to Vulkan 1.1. maintenance_3: Option, + + /// Additional `vk::PhysicalDevice` properties from the + /// `VK_EXT_descriptor_indexing` extension, promoted to Vulkan 1.2. descriptor_indexing: Option, + + /// Additional `vk::PhysicalDevice` properties from the + /// `VK_KHR_acceleration_structure` extension. acceleration_structure: Option, + + /// Additional `vk::PhysicalDevice` properties from the + /// `VK_KHR_driver_properties` extension, promoted to Vulkan 1.2. driver: Option, + /// The device API version. /// /// Which is the version of Vulkan supported for device-level functionality. @@ -923,9 +1018,10 @@ impl super::InstanceShared { let mut builder = vk::PhysicalDeviceProperties2KHR::builder(); if supports_maintenance3 { - capabilities.maintenance_3 = - Some(vk::PhysicalDeviceMaintenance3Properties::default()); - builder = builder.push_next(capabilities.maintenance_3.as_mut().unwrap()); + let next = capabilities + .maintenance_3 + .insert(vk::PhysicalDeviceMaintenance3Properties::default()); + builder = builder.push_next(next); } if supports_descriptor_indexing { @@ -1001,7 +1097,8 @@ impl super::InstanceShared { builder = builder.push_next(next); } - // `VK_KHR_imageless_framebuffer` is promoted to 1.2, but has no changes, so we can keep using the extension unconditionally. + // `VK_KHR_imageless_framebuffer` is promoted to 1.2, but has no + // changes, so we can keep using the extension unconditionally. if capabilities.supports_extension(vk::KhrImagelessFramebufferFn::name()) { let next = features .imageless_framebuffer @@ -1009,7 +1106,8 @@ impl super::InstanceShared { builder = builder.push_next(next); } - // `VK_KHR_timeline_semaphore` is promoted to 1.2, but has no changes, so we can keep using the extension unconditionally. + // `VK_KHR_timeline_semaphore` is promoted to 1.2, but has no + // changes, so we can keep using the extension unconditionally. if capabilities.supports_extension(vk::KhrTimelineSemaphoreFn::name()) { let next = features .timeline_semaphore