diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 6c287b927c..b4a6f86548 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -159,11 +159,44 @@ jobs: # check with no features cargo clippy --target ${{ matrix.target }} -p wgpu -p wgpu-core -p wgpu-info -p player --no-default-features - # check with all features - cargo clippy --target ${{ matrix.target }} -p wgpu -p wgpu-core -p wgpu-info -p player --tests --all-features + # Check with all features. + # (But watch out for backend-selection features in wgpu-core; some of + # those only build on the right platforms.) + cargo clippy --target ${{ matrix.target }} -p wgpu -p wgpu-info -p player --tests --all-features + cargo clippy --target ${{ matrix.target }} -p wgpu-core --tests \ + --features="portable_features" # build docs - cargo doc --target ${{ matrix.target }} -p wgpu -p wgpu-core -p wgpu-info -p player --all-features --no-deps + # (Watch out for backend-selection features in wgpu-core; some of + # those only build on the right platforms.) + cargo doc --target ${{ matrix.target }} -p wgpu -p wgpu-info -p player --all-features --no-deps + cargo doc --target ${{ matrix.target }} -p wgpu-core --no-deps --features="portable_features" + + wasm-test: + name: Test WebAssembly + runs-on: ubuntu-latest + steps: + - name: checkout repo + uses: actions/checkout@v3 + + - name: install rust stable + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + + - name: install wasm-pack # install from fork until this is merged: https://github.com/rustwasm/wasm-pack/pull/1185 + run: | + # replace with "install wasm-pack action", which doesn't work for this project because of https://github.com/rustwasm/wasm-pack/issues/1180 + # - name: install wasm-pack + # uses: jetli/wasm-pack-action@v0.4.0 + cargo install --git https://github.com/haraldreingruber/wasm-pack wasm-pack + + - name: execute tests + run: | + cd wgpu + wasm-pack test --headless --chrome --features webgl gpu-test: strategy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 96f651e8e1..60150d709a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,42 @@ Bottom level categories: ### Major Changes +#### Backend selection by features + +Whereas `wgpu-core` used to automatically select backends to enable +based on the target OS and architecture, it now has separate features +to enable each backend: + +- "metal", for the Metal API on macOS and iOS +- "vulkan", for the Vulkan API (Linux, some Android, and occasionally Windows) +- "dx12", for Microsoft's Direct3D 12 API +- "gles", OpenGL ES, available on many systems +- "dx11", for Microsoft's Direct3D 11 API + +None are enabled by default, but the `wgpu` crate automatically +selects these features based on the target operating system and +architecture, using the same rules that `wgpu-core` used to, so users +of `wgpu` should be unaffected by this change. However, other crates +using `wgpu-core` directly will need to copy `wgpu`'s logic or write +their own. See the `[target]` section of `wgpu/Cargo.toml` for +details. + +Similarly, `wgpu-core` now has `emscripten` and `renderdoc` features +that `wgpu` enables on appropriate platforms. + +In previous releases, the `wgpu-core` crate decided which backends to +support. However, this left `wgpu-core`'s users with no way to +override those choices. (Firefox doesn't want the GLES back end, for +example.) There doesn't seem to be any way to have a crate select +backends based on target OS and architecture that users of that crate +can still override. Default features can't be selected based on the +target, for example. That implies that we should do the selection as +late in the dependency DAG as feasible. Having `wgpu` (and +`wgpu-core`'s other dependents) choose backends seems like the best +option. + +By @jimblandy in [#3254](https://github.com/gfx-rs/wgpu/pull/3254). + #### Surface Capabilities API The various surface capability functions were combined into a single call that gives you all the capabilities. @@ -63,6 +99,10 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non + let config = surface.get_default_config(&adapter).expect("Surface unsupported by adapter"); ``` +#### Fallible surface creation + +`Instance::create_surface()` now returns `Result` instead of `Surface`. This allows an error to be returned instead of panicking if the given window is a HTML canvas and obtaining a WebGPU or WebGL 2 context fails. (No other platforms currently report any errors through this path.) By @kpreid in [#3052](https://github.com/gfx-rs/wgpu/pull/3052/) + ### Changes #### General @@ -73,6 +113,7 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non - New downlevel feature `UNRESTRICTED_INDEX_BUFFER` to indicate support for using `INDEX` together with other non-copy/map usages (unsupported on WebGL). By @Wumpf in [#3157](https://github.com/gfx-rs/wgpu/pull/3157) - Combine `Surface::get_supported_formats`, `Surface::get_supported_present_modes`, and `Surface::get_supported_alpha_modes` into `Surface::get_capabilities` and `SurfaceCapabilities`. By @cwfitzgerald in [#3157](https://github.com/gfx-rs/wgpu/pull/3157) - Make `Surface::get_default_config` return an Option to prevent panics. By @cwfitzgerald in [#3157](https://github.com/gfx-rs/wgpu/pull/3157) +- Lower the `max_buffer_size` limit value for compatibility with Apple2 and WebGPU compliance. By @jinleili in [#3255](https://github.com/gfx-rs/wgpu/pull/3255) #### WebGPU @@ -92,11 +133,13 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non - Add `get_default_config` for `Surface` to simplify user creation of `SurfaceConfiguration`. By @jinleili in [#3034](https://github.com/gfx-rs/wgpu/pull/3034) - Native adapters can now use MSAA x2 and x8 if it's supported , previously only x1 and x4 were supported . By @39ali in [3140](https://github.com/gfx-rs/wgpu/pull/3140) - Implemented correleation between user timestamps and platform specific presentation timestamps via [`Adapter::correlate_presentation_timestamp`]. By @cwfitzgerald in [#3240](https://github.com/gfx-rs/wgpu/pull/3240) +- Added support for `Features::SHADER_PRIMITIVE_INDEX` on all backends. By @cwfitzgerald in [#3272](https://github.com/gfx-rs/wgpu/pull/3272) #### GLES - Surfaces support now `TextureFormat::Rgba8Unorm` and (non-web only) `TextureFormat::Bgra8Unorm`. By @Wumpf in [#3070](https://github.com/gfx-rs/wgpu/pull/3070) - Support alpha to coverage. By @Wumpf in [#3156](https://github.com/gfx-rs/wgpu/pull/3156) +- Support filtering f32 textures. By @expenses in [#3261](https://github.com/gfx-rs/wgpu/pull/3261) #### WebGPU @@ -115,6 +158,8 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non - Fix an integer overflow in `queue_write_texture` by @nical in (#3146)[https://github.com/gfx-rs/wgpu/pull/3146] - Make `RenderPassCompatibilityError` and `CreateShaderModuleError` not so huge. By @jimblandy in (#3226)[https://github.com/gfx-rs/wgpu/pull/3226] - 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) #### WebGPU @@ -136,6 +181,10 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non - Let the wgpu examples `framework.rs` compile again under Emscripten. By @jimblandy in [#3246](https://github.com/gfx-rs/wgpu/pull/3246) +#### Vulkan + +- Update ash to 0.37.1+1.3.235 to fix CI breaking by changing a call to the deprecated `debug_utils_set_object_name()` function to `set_debug_utils_object_name()` by @elabajaba in [#3273](https://github.com/gfx-rs/wgpu/pull/3273) + ### Examples - Log adapter info in hello example on wasm target by @JolifantoBambla in [#2858](https://github.com/gfx-rs/wgpu/pull/2858) @@ -143,8 +192,9 @@ Additionally `Surface::get_default_config` now returns an Option and returns Non ### Testing/Internal - Update the `minimum supported rust version` to 1.64 -- Use cargo 1.64 workspace inheritance feature. By @jinleili in [#3107](https://github.com/gfx-rs/wgpu/pull/3107) - Move `ResourceMetadata` into its own module. By @jimblandy in [#3213](https://github.com/gfx-rs/wgpu/pull/3213) +- Add WebAssembly testing infrastructure. By @haraldreingruber in [#3238](https://github.com/gfx-rs/wgpu/pull/3238) +- Error message when you forget to use cargo-nextest. By @cwfitzgerald in [#3293](https://github.com/gfx-rs/wgpu/pull/3293) #### Vulkan diff --git a/Cargo.lock b/Cargo.lock index e68dd9d134..14720620d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -66,9 +66,9 @@ dependencies = [ [[package]] name = "ash" -version = "0.37.0+1.3.209" +version = "0.37.1+1.3.235" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "006ca68e0f2b03f22d6fa9f2860f85aed430d257fec20f8879b2145e7c7ae1a6" +checksum = "911015c962d56e2e4052f40182ca5462ba60a3d2ff04e827c365a0ab3d65726d" dependencies = [ "libloading", ] @@ -131,12 +131,6 @@ dependencies = [ "safemem", ] -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - [[package]] name = "base64" version = "0.13.0" @@ -145,9 +139,9 @@ checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" [[package]] name = "base64-simd" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "278c7ba87265587c4823cf1b2fdf57834151540b2e509574adb03627f8c7f22d" +checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" dependencies = [ "simd-abstraction", ] @@ -259,12 +253,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cgl" version = "0.3.2" @@ -523,20 +511,21 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.69.0" +version = "0.80.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02cd684bd0097101aa8b411dac1e955ded1bc9c0945a453b607af929ca33b380" +checksum = "68a13f7497eaec2c5b7f766d7cecbdd8547611dabd6ce8f615c507d275887049" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.151.0" +version = "0.162.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "390bdf983f9f20d403b09894ce4d9baeb980fa5faa33bf859c47ffa729abd157" +checksum = "3f2aa18d22faadc44c9c287b4d6f5a0f2aeea75a120d2a7d400a98e4b9255584" dependencies = [ "anyhow", + "bytes", "deno_ops", "futures", "indexmap", @@ -548,6 +537,7 @@ dependencies = [ "serde", "serde_json", "serde_v8", + "smallvec", "sourcemap", "url", "v8", @@ -555,11 +545,12 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.29.0" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e5775de06dc4589c43fa5d81d962f8db9640ccf214291625300e6bf6f3e806" +checksum = "83ddd1b980dcf7c7b4ad586338704c78c6423608f4b8fd622d72bfa76006333f" dependencies = [ "once_cell", + "pmutil", "proc-macro-crate", "proc-macro2", "quote", @@ -569,9 +560,9 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.69.0" +version = "0.80.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fcc823c3e01d24a2a55e8e9b23fcdcfdf376c039a24b3e3571b9b17630f05186" +checksum = "4d0cf9b99857820d594cc816b84507ba062f276427aab815c380cdeec6554021" dependencies = [ "deno_core", "serde", @@ -581,9 +572,9 @@ dependencies = [ [[package]] name = "deno_web" -version = "0.100.0" +version = "0.111.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "204620cb1ce3ec06b74d51fb3370c4fcb25d34d42b0557b6e97bbe84ea64e770" +checksum = "54f0a00df81eee23798d2a516bdc54d7541e3008310b26e7f79533df7d22bd7b" dependencies = [ "async-trait", "base64-simd", @@ -597,7 +588,7 @@ dependencies = [ [[package]] name = "deno_webgpu" -version = "0.63.0" +version = "0.81.0" dependencies = [ "deno_core", "serde", @@ -608,9 +599,9 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.69.0" +version = "0.80.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3bbfcb5416924c4b7ed50514d6577d8a87a61772a043daabe00d81734f5cb07" +checksum = "92a564d515fb807b1f2712717b4de5082cc59f3f86a7e233c04231a760c001e5" dependencies = [ "deno_core", ] @@ -1257,9 +1248,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.134" +version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "329c933548736bc49fd575ee68c89e8be4d260064184389a5b77517cddd99ffb" +checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" [[package]] name = "libloading" @@ -1558,9 +1549,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.15.0" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e82dad04139b71a90c080c8463fe0dc7902db5192d939bd0950f074d014339e1" +checksum = "2f7254b99e31cad77da24b08ebf628882739a608578bb1bcdfc1f9c21260d7c0" [[package]] name = "osmesa-sys" @@ -1571,6 +1562,12 @@ dependencies = [ "shared_library", ] +[[package]] +name = "outref" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" + [[package]] name = "parking" version = "2.0.0" @@ -1699,6 +1696,17 @@ dependencies = [ "winit", ] +[[package]] +name = "pmutil" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3894e5d549cccbe44afecf72922f277f603cd4bb0219c8342631ef18fffbe004" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "png" version = "0.17.6" @@ -1998,9 +2006,9 @@ checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" [[package]] name = "serde" -version = "1.0.145" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728eb6351430bccb993660dfffc5a72f91ccc1295abaa8ce19b27ebe4f75568b" +checksum = "0f747710de3dcd43b88c9168773254e809d8ddbdf9653b84e2554ab219f17860" dependencies = [ "serde_derive", ] @@ -2016,9 +2024,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.145" +version = "1.0.144" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fa1584d3d1bcacd84c277a0dfe21f5b0f6accf4a23d04d4c6d61f1af522b4c" +checksum = "94ed3a816fb1d101812f83e789f888322c34e291f894f19590dc310963e87a00" dependencies = [ "proc-macro2", "quote", @@ -2050,9 +2058,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.62.0" +version = "0.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f30310f753d4b1347acdd669a30b8e6208029cfbb28d3d91012b19333eeff1" +checksum = "17891f7f7e138e2d25fafd19b5f7a95c2cf1e72be0e6804343c63aa6e90b0287" dependencies = [ "bytes", "derive_more", @@ -2104,9 +2112,12 @@ dependencies = [ [[package]] name = "simd-abstraction" -version = "0.6.2" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2880f3f7b392823ee65bbcc681961cd8e698c6a30e91ab9b4eef1f9c6c226d8" +checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" +dependencies = [ + "outref", +] [[package]] name = "slab" @@ -2163,11 +2174,11 @@ dependencies = [ [[package]] name = "sourcemap" -version = "6.0.1" +version = "6.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e031f2463ecbdd5f34c950f89f5c1e1032f22c0f8e3dc4bdb2e8b6658cf61eb" +checksum = "c46fdc1838ff49cf692226f5c2b0f5b7538f556863d0eca602984714667ac6e7" dependencies = [ - "base64 0.11.0", + "base64 0.13.0", "if_chain", "lazy_static", "regex", @@ -2295,9 +2306,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.21.2" +version = "1.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e03c497dc955702ba729190dc4aac6f2a0ce97f913e5b1b5912fc5039d9099" +checksum = "0020c875007ad96677dcc890298f4b942882c5d4eb7cc8f439fc3bf813dc9c95" dependencies = [ "autocfg", "bytes", @@ -2305,6 +2316,7 @@ dependencies = [ "memchr", "mio", "num_cpus", + "once_cell", "parking_lot 0.12.1", "pin-project-lite", "signal-hook-registry", @@ -2440,9 +2452,9 @@ dependencies = [ [[package]] name = "uuid" -version = "1.2.1" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "feb41e78f93363bb2df8b0e86a2ca30eed7806ea16ea0c790d757cf93f79be83" +checksum = "dd6469f4314d5f1ffec476e05f17cc9a78bc7a27a6a857842170bdf8d6f98d2f" dependencies = [ "getrandom 0.2.7", "serde", @@ -2450,14 +2462,13 @@ dependencies = [ [[package]] name = "v8" -version = "0.49.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a1cbad73336d67babcbe5e3b03c907c8d2ff77fc6f997570af219bbd9fdb6ce" +checksum = "8e9b88668afedf6ec9f8f6d30b446f622498da2ef0b3991a52e10f0ea8c6cc09" dependencies = [ "bitflags", "fslock", "lazy_static", - "libc", "which", ] @@ -2627,6 +2638,30 @@ version = "0.2.83" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f" +[[package]] +name = "wasm-bindgen-test" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09d2fff962180c3fadf677438054b1db62bee4aa32af26a45388af07d1287e1d" +dependencies = [ + "console_error_panic_hook", + "js-sys", + "scoped-tls", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-bindgen-test-macro", +] + +[[package]] +name = "wasm-bindgen-test-macro" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4683da3dfc016f704c9f82cf401520c4f1cb3ee440f7f52b3d6ac29506a49ca7" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "wasm-bindgen-threads-xform" version = "0.2.83" @@ -2804,6 +2839,7 @@ dependencies = [ "static_assertions", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-bindgen-test", "web-sys", "wgpu-core", "wgpu-hal", @@ -2818,7 +2854,6 @@ dependencies = [ "arrayvec 0.7.2", "bit-vec", "bitflags", - "cfg_aliases", "codespan-reporting", "fxhash", "log", diff --git a/Cargo.toml b/Cargo.toml index 01ea9934e1..a2d537a0ff 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -89,7 +89,7 @@ objc = "0.2.5" core-graphics-types = "0.1" # Vulkan dependencies -ash = "0.37" +ash = "0.37.1" gpu-alloc = "0.5" gpu-descriptor = "0.2" android_system_properties = "0.1.1" @@ -113,14 +113,15 @@ console_log = "0.2" js-sys = "0.3.60" wasm-bindgen = "0.2.83" wasm-bindgen-futures = "0.4.33" +wasm-bindgen-test = "0.3" web-sys = "0.3.60" # deno dependencies -deno_console = "0.69.0" -deno_core = "0.151.0" -deno_url = "0.69.0" -deno_web = "0.100.0" -deno_webidl = "0.69.0" +deno_console = "0.80.0" +deno_core = "0.162.0" +deno_url = "0.80.0" +deno_web = "0.111.0" +deno_webidl = "0.80.0" deno_webgpu = { path = "./deno_webgpu" } tokio = "1.19.0" termcolor = "1.1.2" diff --git a/deno_webgpu/Cargo.toml b/deno_webgpu/Cargo.toml index 3091c9c088..4a47a9418e 100644 --- a/deno_webgpu/Cargo.toml +++ b/deno_webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.63.0" +version = "0.81.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" @@ -14,5 +14,23 @@ description = "WebGPU implementation for Deno" deno_core.workspace = true serde = { workspace = true, features = ["derive"] } tokio = { workspace = true, features = ["full"] } -wgpu-core = { workspace = true, features = ["trace", "replay", "serde", "strict_asserts", "wgsl"] } wgpu-types = { workspace = true, features = ["trace", "replay", "serde"] } + +[dependencies.wgpu-core] +workspace = true +features = ["trace", "replay", "serde", "strict_asserts", "wgsl", "gles"] + +# We want the wgpu-core Metal backend on macOS and iOS. +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgpu-core] +workspace = true +features = ["metal"] + +# We want the wgpu-core Direct3D backends on Windows. +[target.'cfg(windows)'.dependencies.wgpu-core] +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.wgpu-core] +workspace = true +features = ["vulkan"] diff --git a/deno_webgpu/src/01_webgpu.js b/deno_webgpu/src/01_webgpu.js index c01911af52..373fe58a8b 100644 --- a/deno_webgpu/src/01_webgpu.js +++ b/deno_webgpu/src/01_webgpu.js @@ -10,6 +10,7 @@ ((window) => { const core = window.Deno.core; + const ops = core.ops; const webidl = window.__bootstrap.webidl; const eventTarget = window.__bootstrap.eventTarget; const { DOMException } = window.__bootstrap.domException; @@ -26,12 +27,12 @@ ObjectDefineProperty, ObjectPrototypeIsPrototypeOf, Promise, - PromiseAll, PromisePrototypeCatch, PromisePrototypeThen, PromiseReject, PromiseResolve, SafeArrayIterator, + SafePromiseAll, Set, SetPrototypeEntries, SetPrototypeForEach, @@ -946,8 +947,7 @@ context: "Argument 1", }); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync( - "op_webgpu_create_buffer", + const { rid, err } = ops.op_webgpu_create_buffer( device.rid, descriptor.label, descriptor.size, @@ -997,7 +997,7 @@ context: "Argument 1", }); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync("op_webgpu_create_texture", { + const { rid, err } = ops.op_webgpu_create_texture({ deviceRid: device.rid, ...descriptor, size: normalizeGPUExtent3D(descriptor.size), @@ -1025,7 +1025,7 @@ context: "Argument 1", }); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync("op_webgpu_create_sampler", { + const { rid, err } = ops.op_webgpu_create_texture({ deviceRid: device.rid, ...descriptor, }); @@ -1065,8 +1065,7 @@ } } - const { rid, err } = core.opSync( - "op_webgpu_create_bind_group_layout", + const { rid, err } = ops.op_webgpu_create_bind_group_layout( device.rid, descriptor.label, descriptor.entries, @@ -1108,8 +1107,7 @@ return rid; }, ); - const { rid, err } = core.opSync( - "op_webgpu_create_pipeline_layout", + const { rid, err } = ops.op_webgpu_create_pipeline_layout( device.rid, descriptor.label, bindGroupLayouts, @@ -1203,8 +1201,7 @@ } }); - const { rid, err } = core.opSync( - "op_webgpu_create_bind_group", + const { rid, err } = ops.op_webgpu_create_bind_group( device.rid, descriptor.label, layout, @@ -1233,8 +1230,7 @@ context: "Argument 1", }); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync( - "op_webgpu_create_shader_module", + const { rid, err } = ops.op_webgpu_create_shader_module( device.rid, descriptor.label, descriptor.code, @@ -1283,8 +1279,7 @@ selfContext: "this", }); - const { rid, err } = core.opSync( - "op_webgpu_create_compute_pipeline", + const { rid, err } = ops.op_webgpu_create_compute_pipeline( device.rid, descriptor.label, layout, @@ -1355,7 +1350,7 @@ }; } - const { rid, err } = core.opSync("op_webgpu_create_render_pipeline", { + const { rid, err } = ops.op_webgpu_create_render_pipeline({ deviceRid: device.rid, label: descriptor.label, layout, @@ -1402,8 +1397,7 @@ context: "Argument 1", }); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync( - "op_webgpu_create_command_encoder", + const { rid, err } = ops.op_webgpu_create_command_encoder( device.rid, descriptor.label, ); @@ -1435,13 +1429,10 @@ }, ); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync( - "op_webgpu_create_render_bundle_encoder", - { - deviceRid: device.rid, - ...descriptor, - }, - ); + const { rid, err } = ops.op_webgpu_create_render_bundle_encoder({ + deviceRid: device.rid, + ...descriptor, + }); device.pushError(err); const renderBundleEncoder = createGPURenderBundleEncoder( @@ -1469,7 +1460,7 @@ }, ); const device = assertDevice(this, { prefix, context: "this" }); - const { rid, err } = core.opSync("op_webgpu_create_query_set", { + const { rid, err } = ops.op_webgpu_create_query_set({ deviceRid: device.rid, ...descriptor, }); @@ -1530,7 +1521,7 @@ "OperationError", ); } - const operations = PromiseAll(scope.operations); + const operations = SafePromiseAll(scope.operations); return PromisePrototypeThen( operations, () => PromiseResolve(null), @@ -1600,11 +1591,7 @@ return rid; }, ); - const { err } = core.opSync( - "op_webgpu_queue_submit", - device.rid, - commandBufferRids, - ); + const { err } = ops.op_webgpu_queue_submit(device.rid, commandBufferRids); for (const commandBuffer of commandBuffers) { commandBuffer[_rid] = undefined; } @@ -1659,8 +1646,7 @@ selfContext: "this", resourceContext: "Argument 1", }); - const { err } = core.opSync( - "op_webgpu_write_buffer", + const { err } = ops.op_webgpu_write_buffer( device.rid, bufferRid, bufferOffset, @@ -1707,8 +1693,7 @@ selfContext: "this", resourceContext: "texture", }); - const { err } = core.opSync( - "op_webgpu_write_texture", + const { err } = ops.op_webgpu_write_texture( device.rid, { texture: textureRid, @@ -1979,8 +1964,7 @@ } const buffer = new ArrayBuffer(rangeSize); - const { rid } = core.opSync( - "op_webgpu_buffer_get_mapped_range", + const { rid } = ops.op_webgpu_buffer_get_mapped_range( bufferRid, offset, size, @@ -2034,8 +2018,7 @@ throw new DOMException(`${prefix}: invalid state.`, "OperationError"); } for (const [buffer, mappedRid] of mappedRanges) { - const { err } = core.opSync( - "op_webgpu_buffer_unmap", + const { err } = ops.op_webgpu_buffer_unmap( bufferRid, mappedRid, ...new SafeArrayIterator(write ? [new Uint8Array(buffer)] : []), @@ -2198,7 +2181,7 @@ }); const device = assertDevice(this, { prefix, context: "this" }); const textureRid = assertResource(this, { prefix, context: "this" }); - const { rid, err } = core.opSync("op_webgpu_create_texture_view", { + const { rid, err } = ops.op_webgpu_create_texture_view({ textureRid, ...descriptor, }); @@ -2622,11 +2605,11 @@ prefix, context: "this", }); - const { rid, label, err } = core.opSync( - "op_webgpu_compute_pipeline_get_bind_group_layout", - computePipelineRid, - index, - ); + const { rid, label, err } = + ops.op_webgpu_compute_pipeline_get_bind_group_layout( + computePipelineRid, + index, + ); device.pushError(err); const bindGroupLayout = createGPUBindGroupLayout( @@ -2699,11 +2682,11 @@ prefix, context: "this", }); - const { rid, label, err } = core.opSync( - "op_webgpu_render_pipeline_get_bind_group_layout", - renderPipelineRid, - index, - ); + const { rid, label, err } = + ops.op_webgpu_render_pipeline_get_bind_group_layout( + renderPipelineRid, + index, + ); device.pushError(err); const bindGroupLayout = createGPUBindGroupLayout( @@ -2893,8 +2876,7 @@ }, ); - const { rid } = core.opSync( - "op_webgpu_command_encoder_begin_render_pass", + const { rid } = ops.op_webgpu_command_encoder_begin_render_pass( commandEncoderRid, descriptor.label, colorAttachments, @@ -2928,8 +2910,7 @@ context: "this", }); - const { rid } = core.opSync( - "op_webgpu_command_encoder_begin_compute_pass", + const { rid } = ops.op_webgpu_command_encoder_begin_compute_pass( commandEncoderRid, descriptor.label, ); @@ -3005,8 +2986,7 @@ selfContext: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_copy_buffer_to_buffer", + const { err } = ops.op_webgpu_command_encoder_copy_buffer_to_buffer( commandEncoderRid, sourceRid, sourceOffset, @@ -3063,8 +3043,7 @@ selfContext: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_copy_buffer_to_texture", + const { err } = ops.op_webgpu_command_encoder_copy_buffer_to_texture( commandEncoderRid, { ...source, @@ -3128,8 +3107,7 @@ resourceContext: "buffer in Argument 2", selfContext: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_copy_texture_to_buffer", + const { err } = ops.op_webgpu_command_encoder_copy_texture_to_buffer( commandEncoderRid, { texture: sourceTextureRid, @@ -3193,8 +3171,7 @@ resourceContext: "texture in Argument 2", selfContext: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_copy_texture_to_texture", + const { err } = ops.op_webgpu_command_encoder_copy_texture_to_texture( commandEncoderRid, { texture: sourceTextureRid, @@ -3247,8 +3224,7 @@ prefix, context: "Argument 1", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_clear_buffer", + const { err } = ops.op_webgpu_command_encoder_clear_buffer( commandEncoderRid, bufferRid, offset, @@ -3274,8 +3250,7 @@ prefix, context: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_push_debug_group", + const { err } = ops.op_webgpu_command_encoder_push_debug_group( commandEncoderRid, groupLabel, ); @@ -3290,8 +3265,7 @@ prefix, context: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_pop_debug_group", + const { err } = ops.op_webgpu_command_encoder_pop_debug_group( commandEncoderRid, ); device.pushError(err); @@ -3314,8 +3288,7 @@ prefix, context: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_insert_debug_marker", + const { err } = ops.op_webgpu_command_encoder_insert_debug_marker( commandEncoderRid, markerLabel, ); @@ -3353,8 +3326,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_write_timestamp", + const { err } = ops.op_webgpu_command_encoder_write_timestamp( commandEncoderRid, querySetRid, queryIndex, @@ -3423,8 +3395,7 @@ resourceContext: "Argument 3", selfContext: "this", }); - const { err } = core.opSync( - "op_webgpu_command_encoder_resolve_query_set", + const { err } = ops.op_webgpu_command_encoder_resolve_query_set( commandEncoderRid, querySetRid, firstQuery, @@ -3451,8 +3422,7 @@ prefix, context: "this", }); - const { rid, err } = core.opSync( - "op_webgpu_command_encoder_finish", + const { rid, err } = ops.op_webgpu_command_encoder_finish( commandEncoderRid, descriptor.label, ); @@ -3551,7 +3521,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync("op_webgpu_render_pass_set_viewport", { + ops.op_webgpu_render_pass_set_viewport({ renderPassRid, x, y, @@ -3598,8 +3568,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_set_scissor_rect", + ops.op_webgpu_render_pass_set_scissor_rect( renderPassRid, x, y, @@ -3629,8 +3598,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_set_blend_constant", + ops.op_webgpu_render_pass_set_blend_constant( renderPassRid, normalizeGPUColor(color), ); @@ -3657,8 +3625,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_set_stencil_reference", + ops.op_webgpu_render_pass_set_stencil_reference( renderPassRid, reference, ); @@ -3707,8 +3674,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_begin_pipeline_statistics_query", + ops.op_webgpu_render_pass_begin_pipeline_statistics_query( renderPassRid, querySetRid, queryIndex, @@ -3728,10 +3694,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_end_pipeline_statistics_query", - renderPassRid, - ); + ops.op_webgpu_render_pass_end_pipeline_statistics_query(renderPassRid); } /** @@ -3769,8 +3732,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_write_timestamp", + ops.op_webgpu_render_pass_write_timestamp( renderPassRid, querySetRid, queryIndex, @@ -3808,11 +3770,7 @@ }); return rid; }); - core.opSync( - "op_webgpu_render_pass_execute_bundles", - renderPassRid, - bundleRids, - ); + ops.op_webgpu_render_pass_execute_bundles(renderPassRid, bundleRids); } end() { @@ -3827,8 +3785,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - const { err } = core.opSync( - "op_webgpu_render_pass_end", + const { err } = ops.op_webgpu_render_pass_end( commandEncoderRid, renderPassRid, ); @@ -3875,8 +3832,7 @@ dynamicOffsetsDataStart = 0; dynamicOffsetsDataLength = dynamicOffsetsData.length; } - core.opSync( - "op_webgpu_render_pass_set_bind_group", + ops.op_webgpu_render_pass_set_bind_group( renderPassRid, index, bindGroupRid, @@ -3907,11 +3863,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_push_debug_group", - renderPassRid, - groupLabel, - ); + ops.op_webgpu_render_pass_push_debug_group(renderPassRid, groupLabel); } popDebugGroup() { @@ -3927,7 +3879,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync("op_webgpu_render_pass_pop_debug_group", renderPassRid); + ops.op_webgpu_render_pass_pop_debug_group(renderPassRid); } /** @@ -3951,11 +3903,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_insert_debug_marker", - renderPassRid, - markerLabel, - ); + ops.op_webgpu_render_pass_insert_debug_marker(renderPassRid, markerLabel); } /** @@ -3988,11 +3936,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_set_pipeline", - renderPassRid, - pipelineRid, - ); + ops.op_webgpu_render_pass_set_pipeline(renderPassRid, pipelineRid); } /** @@ -4042,8 +3986,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_set_index_buffer", + ops.op_webgpu_render_pass_set_index_buffer( renderPassRid, bufferRid, indexFormat, @@ -4099,8 +4042,7 @@ resourceContext: "Argument 2", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_set_vertex_buffer", + ops.op_webgpu_render_pass_set_vertex_buffer( renderPassRid, slot, bufferRid, @@ -4144,8 +4086,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_draw", + ops.op_webgpu_render_pass_draw( renderPassRid, vertexCount, instanceCount, @@ -4201,8 +4142,7 @@ context: "encoder referenced by this", }); const renderPassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_render_pass_draw_indexed", + ops.op_webgpu_render_pass_draw_indexed( renderPassRid, indexCount, instanceCount, @@ -4247,8 +4187,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_draw_indirect", + ops.op_webgpu_render_pass_draw_indirect( renderPassRid, indirectBufferRid, indirectOffset, @@ -4290,8 +4229,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_pass_draw_indexed_indirect", + ops.op_webgpu_render_pass_draw_indexed_indirect( renderPassRid, indirectBufferRid, indirectOffset, @@ -4374,11 +4312,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_compute_pass_set_pipeline", - computePassRid, - pipelineRid, - ); + ops.op_webgpu_compute_pass_set_pipeline(computePassRid, pipelineRid); } /** @@ -4416,8 +4350,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_compute_pass_dispatch_workgroups", + ops.op_webgpu_compute_pass_dispatch_workgroups( computePassRid, workgroupCountX, workgroupCountY, @@ -4460,8 +4393,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_compute_pass_dispatch_workgroups_indirect", + ops.op_webgpu_compute_pass_dispatch_workgroups_indirect( computePassRid, indirectBufferRid, indirectOffset, @@ -4503,8 +4435,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_compute_pass_begin_pipeline_statistics_query", + ops.op_webgpu_compute_pass_begin_pipeline_statistics_query( computePassRid, querySetRid, queryIndex, @@ -4524,10 +4455,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_compute_pass_end_pipeline_statistics_query", - computePassRid, - ); + ops.op_webgpu_compute_pass_end_pipeline_statistics_query(computePassRid); } /** @@ -4565,8 +4493,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_compute_pass_write_timestamp", + ops.op_webgpu_compute_pass_write_timestamp( computePassRid, querySetRid, queryIndex, @@ -4585,8 +4512,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - const { err } = core.opSync( - "op_webgpu_compute_pass_end", + const { err } = ops.op_webgpu_compute_pass_end( commandEncoderRid, computePassRid, ); @@ -4633,8 +4559,7 @@ dynamicOffsetsDataStart = 0; dynamicOffsetsDataLength = dynamicOffsetsData.length; } - core.opSync( - "op_webgpu_compute_pass_set_bind_group", + ops.op_webgpu_compute_pass_set_bind_group( computePassRid, index, bindGroupRid, @@ -4665,11 +4590,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_compute_pass_push_debug_group", - computePassRid, - groupLabel, - ); + ops.op_webgpu_compute_pass_push_debug_group(computePassRid, groupLabel); } popDebugGroup() { @@ -4685,7 +4606,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - core.opSync("op_webgpu_compute_pass_pop_debug_group", computePassRid); + ops.op_webgpu_compute_pass_pop_debug_group(computePassRid); } /** @@ -4709,8 +4630,7 @@ context: "encoder referenced by this", }); const computePassRid = assertResource(this, { prefix, context: "this" }); - core.opSync( - "op_webgpu_compute_pass_insert_debug_marker", + ops.op_webgpu_compute_pass_insert_debug_marker( computePassRid, markerLabel, ); @@ -4820,8 +4740,7 @@ prefix, context: "this", }); - const { rid, err } = core.opSync( - "op_webgpu_render_bundle_encoder_finish", + const { rid, err } = ops.op_webgpu_render_bundle_encoder_finish( renderBundleEncoderRid, descriptor.label, ); @@ -4872,8 +4791,7 @@ dynamicOffsetsDataStart = 0; dynamicOffsetsDataLength = dynamicOffsetsData.length; } - core.opSync( - "op_webgpu_render_bundle_encoder_set_bind_group", + ops.op_webgpu_render_bundle_encoder_set_bind_group( renderBundleEncoderRid, index, bindGroupRid, @@ -4900,8 +4818,7 @@ prefix, context: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_push_debug_group", + ops.op_webgpu_render_bundle_encoder_push_debug_group( renderBundleEncoderRid, groupLabel, ); @@ -4916,8 +4833,7 @@ prefix, context: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_pop_debug_group", + ops.op_webgpu_render_bundle_encoder_pop_debug_group( renderBundleEncoderRid, ); } @@ -4939,8 +4855,7 @@ prefix, context: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_insert_debug_marker", + ops.op_webgpu_render_bundle_encoder_insert_debug_marker( renderBundleEncoderRid, markerLabel, ); @@ -4972,8 +4887,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_set_pipeline", + ops.op_webgpu_render_bundle_encoder_set_pipeline( renderBundleEncoderRid, pipelineRid, ); @@ -5020,8 +4934,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_set_index_buffer", + ops.op_webgpu_render_bundle_encoder_set_index_buffer( renderBundleEncoderRid, bufferRid, indexFormat, @@ -5071,8 +4984,7 @@ resourceContext: "Argument 2", selfContext: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_set_vertex_buffer", + ops.op_webgpu_render_bundle_encoder_set_vertex_buffer( renderBundleEncoderRid, slot, bufferRid, @@ -5112,8 +5024,7 @@ prefix, context: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_draw", + ops.op_webgpu_render_bundle_encoder_draw( renderBundleEncoderRid, vertexCount, instanceCount, @@ -5165,8 +5076,7 @@ prefix, context: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_draw_indexed", + ops.op_webgpu_render_bundle_encoder_draw_indexed( renderBundleEncoderRid, indexCount, instanceCount, @@ -5207,8 +5117,7 @@ resourceContext: "Argument 1", selfContext: "this", }); - core.opSync( - "op_webgpu_render_bundle_encoder_draw_indirect", + ops.op_webgpu_render_bundle_encoder_draw_indirect( renderBundleEncoderRid, indirectBufferRid, indirectOffset, diff --git a/deno_webgpu/webgpu.idl b/deno_webgpu/webgpu.idl index f0fec0b1b9..30b99a67c8 100644 --- a/deno_webgpu/webgpu.idl +++ b/deno_webgpu/webgpu.idl @@ -790,6 +790,23 @@ dictionary GPUVertexAttribute { required GPUIndex32 shaderLocation; }; +dictionary GPUImageDataLayout { + GPUSize64 offset = 0; + GPUSize32 bytesPerRow; + GPUSize32 rowsPerImage; +}; + +dictionary GPUImageCopyBuffer : GPUImageDataLayout { + required GPUBuffer buffer; +}; + +dictionary GPUImageCopyTexture { + required GPUTexture texture; + GPUIntegerCoordinate mipLevel = 0; + GPUOrigin3D origin = {}; + GPUTextureAspect aspect = "all"; +}; + [Exposed=(Window, DedicatedWorker), SecureContext] interface GPUCommandBuffer { }; @@ -851,23 +868,6 @@ GPUCommandEncoder includes GPUDebugCommandsMixin; dictionary GPUCommandEncoderDescriptor : GPUObjectDescriptorBase { }; -dictionary GPUImageDataLayout { - GPUSize64 offset = 0; - GPUSize32 bytesPerRow; - GPUSize32 rowsPerImage; -}; - -dictionary GPUImageCopyBuffer : GPUImageDataLayout { - required GPUBuffer buffer; -}; - -dictionary GPUImageCopyTexture { - required GPUTexture texture; - GPUIntegerCoordinate mipLevel = 0; - GPUOrigin3D origin = {}; - GPUTextureAspect aspect = "all"; -}; - interface mixin GPUBindingCommandsMixin { undefined setBindGroup(GPUIndex32 index, GPUBindGroup bindGroup, optional sequence dynamicOffsets = []); @@ -972,7 +972,7 @@ enum GPUStoreOp { "discard" }; -dictionary GPURenderPassLayout: GPUObjectDescriptorBase { +dictionary GPURenderPassLayout : GPUObjectDescriptorBase { required sequence colorFormats; GPUTextureFormat depthStencilFormat; GPUSize32 sampleCount = 1; diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index c92865b73d..fb7c90d5ec 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wgpu-core" -version.workspace = true -authors.workspace = true -edition.workspace = true +version = "0.14.0" +authors = ["wgpu developers"] +edition = "2021" description = "WebGPU core logic on wgpu-hal" -homepage.workspace = true -repository.workspace = true -keywords.workspace = true -license.workspace = true +homepage = "https://wgpu.rs/" +repository = "https://github.com/gfx-rs/wgpu" +keywords = ["graphics"] +license = "MIT OR Apache-2.0" [package.metadata.docs.rs] all-features = true @@ -17,6 +17,21 @@ rustdoc-args = ["--cfg", "docsrs"] [features] default = [] + +# Backends, passed through to wgpu-hal +metal = ["hal/metal"] +vulkan = ["hal/vulkan"] +gles = ["hal/gles"] +dx11 = ["hal/dx11"] +dx12 = ["hal/dx12"] + +# Support the Renderdoc graphics debugger: +# https://renderdoc.org/ +renderdoc = ["hal/renderdoc"] + +# Compile for the Emscripten POSIX-in-a-web-page emulation environment. +emscripten = ["hal/emscripten"] + # Apply run-time checks, even in release builds. These are in addition # to the validation carried out at public APIs in all builds. strict_asserts = [] @@ -32,49 +47,38 @@ id32 = [] 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"] + [dependencies] -arrayvec.workspace = true -bitflags.workspace = true -bit-vec.workspace = true -codespan-reporting.workspace = true -fxhash.workspace = true -log.workspace = true -parking_lot.workspace = true -profiling.workspace = true -raw-window-handle = { workspace = true, optional = true } -ron = { workspace = true, optional = true } -serde = { workspace = true, features = ["serde_derive"], optional = true } -smallvec.workspace = true -thiserror.workspace = true +arrayvec = "0.7" +bitflags = "1" +bit-vec = "0.6" +codespan-reporting = "0.11" +fxhash = "0.2.1" +log = "0.4" +# parking_lot 0.12 switches from `winapi` to `windows`; permit either +parking_lot = ">=0.11,<0.13" +profiling = { version = "1", default-features = false } +raw-window-handle = { version = "0.5", optional = true } +ron = { version = "0.8", optional = true } +serde = { version = "1", features = ["serde_derive"], optional = true } +smallvec = "1" +thiserror = "1" [dependencies.naga] -workspace = true +git = "https://github.com/gfx-rs/naga" +rev = "e7fc8e6" +version = "0.10" features = ["clone", "span", "validate"] [dependencies.wgt] -workspace = true +package = "wgpu-types" +path = "../wgpu-types" [dependencies.hal] -workspace = true +package = "wgpu-hal" +path = "../wgpu-hal" [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -web-sys = { workspace = true, features = ["HtmlCanvasElement", "OffscreenCanvas"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -hal = { workspace = true, features = ["gles"] } - -[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies] -hal = { workspace = true, features = ["metal"] } -#Note: could also enable "vulkan" for Vulkan Portability - -[target.'cfg(all(not(target_arch = "wasm32"), unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies] -hal = { workspace = true, features = ["vulkan", "gles", "renderdoc"] } - -[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies] -hal = { workspace = true, features = ["vulkan", "dx12", "dx11", "renderdoc"] } - -[target.'cfg(target_os = "emscripten")'.dependencies] -hal = { workspace = true, features = ["emscripten"] } - -[build-dependencies] -cfg_aliases.workspace = true +web-sys = { version = "0.3.60", features = ["HtmlCanvasElement", "OffscreenCanvas"] } diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs deleted file mode 100644 index 445ea245d2..0000000000 --- a/wgpu-core/build.rs +++ /dev/null @@ -1,22 +0,0 @@ -fn main() { - // Setup cfg aliases - cfg_aliases::cfg_aliases! { - // Vendors/systems - wasm: { target_arch = "wasm32" }, - apple: { any(target_os = "ios", target_os = "macos") }, - unix_wo_apple: {all(unix, not(apple))}, - - // Backends - vulkan: { all(not(wasm), any(windows, unix_wo_apple, feature = "vulkan-portability")) }, - metal: { all(not(wasm), apple) }, - dx12: { all(not(wasm), windows) }, - dx11: { all(not(wasm), windows) }, - gl: { - any( - unix_wo_apple, - feature = "angle", - wasm - ) - }, - } -} diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 5feef3bab4..d64d563b25 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -683,23 +683,56 @@ pub enum BindingResource<'a> { #[derive(Clone, Debug, Error)] pub enum BindError { - #[error("number of dynamic offsets ({actual}) doesn't match the number of dynamic bindings in the bind group layout ({expected})")] - MismatchedDynamicOffsetCount { actual: usize, expected: usize }, #[error( - "dynamic binding at index {idx}: offset {offset} does not respect device's requested `{limit_name}` limit {alignment}" + "Bind group {group} expects {expected} dynamic offset{s0}. However {actual} dynamic offset{s1} were provided.", + s0 = if *.expected >= 2 { "s" } else { "" }, + s1 = if *.actual >= 2 { "s" } else { "" }, + )] + MismatchedDynamicOffsetCount { + group: u8, + actual: usize, + expected: usize, + }, + #[error( + "Dynamic binding index {idx} (targeting bind group {group}, binding {binding}) with value {offset}, does not respect device's requested `{limit_name}` limit: {alignment}" )] UnalignedDynamicBinding { idx: usize, + group: u8, + binding: u32, offset: u32, alignment: u32, limit_name: &'static str, }, - #[error("dynamic binding at index {idx} with offset {offset} would overrun the buffer (limit: {max})")] - DynamicBindingOutOfBounds { idx: usize, offset: u32, max: u64 }, + #[error( + "Dynamic binding offset index {idx} with offset {offset} would overrun the buffer bound to bind group {group} -> binding {binding}. \ + Buffer size is {buffer_size} bytes, the binding binds bytes {binding_range:?}, meaning the maximum the binding can be offset is {maximum_dynamic_offset} bytes", + )] + DynamicBindingOutOfBounds { + idx: usize, + group: u8, + binding: u32, + offset: u32, + buffer_size: wgt::BufferAddress, + binding_range: Range, + maximum_dynamic_offset: wgt::BufferAddress, + }, } #[derive(Debug)] pub struct BindGroupDynamicBindingData { + /// The index of the binding. + /// + /// Used for more descriptive errors. + pub(crate) binding_idx: u32, + /// The size of the buffer. + /// + /// Used for more descriptive errors. + pub(crate) buffer_size: wgt::BufferAddress, + /// The range that the binding covers. + /// + /// Used for more descriptive errors. + pub(crate) binding_range: Range, /// The maximum value the dynamic offset can have before running off the end of the buffer. pub(crate) maximum_dynamic_offset: wgt::BufferAddress, /// The binding type. @@ -739,11 +772,13 @@ pub struct BindGroup { impl BindGroup { pub(crate) fn validate_dynamic_bindings( &self, + bind_group_index: u8, offsets: &[wgt::DynamicOffset], limits: &wgt::Limits, ) -> Result<(), BindError> { if self.dynamic_binding_info.len() != offsets.len() { return Err(BindError::MismatchedDynamicOffsetCount { + group: bind_group_index, expected: self.dynamic_binding_info.len(), actual: offsets.len(), }); @@ -758,6 +793,8 @@ impl BindGroup { let (alignment, limit_name) = buffer_binding_type_alignment(limits, info.binding_type); if offset as wgt::BufferAddress % alignment as u64 != 0 { return Err(BindError::UnalignedDynamicBinding { + group: bind_group_index, + binding: info.binding_idx, idx, offset, alignment, @@ -767,9 +804,13 @@ impl BindGroup { if offset as wgt::BufferAddress > info.maximum_dynamic_offset { return Err(BindError::DynamicBindingOutOfBounds { + group: bind_group_index, + binding: info.binding_idx, idx, offset, - max: info.maximum_dynamic_offset, + buffer_size: info.buffer_size, + binding_range: info.binding_range.clone(), + maximum_dynamic_offset: info.maximum_dynamic_offset, }); } } diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index c228519595..dec6211a73 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -428,7 +428,7 @@ impl Global { .ok_or(ComputePassErrorInner::InvalidBindGroup(bind_group_id)) .map_pass_err(scope)?; bind_group - .validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits) + .validate_dynamic_bindings(index, &temp_offsets, &cmd_buf.limits) .map_pass_err(scope)?; cmd_buf.buffer_memory_init_actions.extend( diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 09af0bbe6a..04e27ec062 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -1238,7 +1238,7 @@ impl Global { .ok_or(RenderCommandError::InvalidBindGroup(bind_group_id)) .map_pass_err(scope)?; bind_group - .validate_dynamic_bindings(&temp_offsets, &cmd_buf.limits) + .validate_dynamic_bindings(index, &temp_offsets, &cmd_buf.limits) .map_pass_err(scope)?; // merge the resource tracker in diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 86c79a9a6d..cdfc0bea4f 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -1732,6 +1732,9 @@ impl Device { // Record binding info for validating dynamic offsets if dynamic { dynamic_binding_info.push(binding_model::BindGroupDynamicBindingData { + binding_idx: binding, + buffer_size: buffer.size, + binding_range: bb.offset..bind_end, maximum_dynamic_offset: buffer.size - bind_end, binding_type: binding_ty, }); @@ -5407,27 +5410,27 @@ impl Global { let mut closures = UserClosures::default(); let mut all_queue_empty = true; - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] { all_queue_empty = self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } - #[cfg(metal)] + #[cfg(feature = "metal")] { all_queue_empty = self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } - #[cfg(dx12)] + #[cfg(feature = "dx12")] { all_queue_empty = self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } - #[cfg(dx11)] + #[cfg(feature = "dx11")] { all_queue_empty = self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; } - #[cfg(gl)] + #[cfg(feature = "gles")] { all_queue_empty = self.poll_devices::(force_wait, &mut closures)? && all_queue_empty; diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index f07010477c..290df3c716 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -1057,30 +1057,30 @@ impl Hub { } pub struct Hubs { - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: Hub, - #[cfg(metal)] + #[cfg(feature = "metal")] metal: Hub, - #[cfg(dx12)] + #[cfg(feature = "dx12")] dx12: Hub, - #[cfg(dx11)] + #[cfg(feature = "dx11")] dx11: Hub, - #[cfg(gl)] + #[cfg(feature = "gles")] gl: Hub, } impl Hubs { fn new(factory: &F) -> Self { Self { - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: Hub::new(factory), - #[cfg(metal)] + #[cfg(feature = "metal")] metal: Hub::new(factory), - #[cfg(dx12)] + #[cfg(feature = "dx12")] dx12: Hub::new(factory), - #[cfg(dx11)] + #[cfg(feature = "dx11")] dx11: Hub::new(factory), - #[cfg(gl)] + #[cfg(feature = "gles")] gl: Hub::new(factory), } } @@ -1089,15 +1089,15 @@ impl Hubs { #[derive(Debug)] pub struct GlobalReport { pub surfaces: StorageReport, - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] pub vulkan: Option, - #[cfg(metal)] + #[cfg(feature = "metal")] pub metal: Option, - #[cfg(dx12)] + #[cfg(feature = "dx12")] pub dx12: Option, - #[cfg(dx11)] + #[cfg(feature = "dx11")] pub dx11: Option, - #[cfg(gl)] + #[cfg(feature = "gles")] pub gl: Option, } @@ -1162,31 +1162,31 @@ impl Global { pub fn generate_report(&self) -> GlobalReport { GlobalReport { surfaces: self.surfaces.data.read().generate_report(), - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: if self.instance.vulkan.is_some() { Some(self.hubs.vulkan.generate_report()) } else { None }, - #[cfg(metal)] + #[cfg(feature = "metal")] metal: if self.instance.metal.is_some() { Some(self.hubs.metal.generate_report()) } else { None }, - #[cfg(dx12)] + #[cfg(feature = "dx12")] dx12: if self.instance.dx12.is_some() { Some(self.hubs.dx12.generate_report()) } else { None }, - #[cfg(dx11)] + #[cfg(feature = "dx11")] dx11: if self.instance.dx11.is_some() { Some(self.hubs.dx11.generate_report()) } else { None }, - #[cfg(gl)] + #[cfg(feature = "gles")] gl: if self.instance.gl.is_some() { Some(self.hubs.gl.generate_report()) } else { @@ -1203,23 +1203,23 @@ impl Drop for Global { let mut surface_guard = self.surfaces.data.write(); // destroy hubs before the instance gets dropped - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] { self.hubs.vulkan.clear(&mut surface_guard, true); } - #[cfg(metal)] + #[cfg(feature = "metal")] { self.hubs.metal.clear(&mut surface_guard, true); } - #[cfg(dx12)] + #[cfg(feature = "dx12")] { self.hubs.dx12.clear(&mut surface_guard, true); } - #[cfg(dx11)] + #[cfg(feature = "dx11")] { self.hubs.dx11.clear(&mut surface_guard, true); } - #[cfg(gl)] + #[cfg(feature = "gles")] { self.hubs.gl.clear(&mut surface_guard, true); } @@ -1261,7 +1261,7 @@ impl HalApi for hal::api::Empty { } } -#[cfg(vulkan)] +#[cfg(feature = "vulkan")] impl HalApi for hal::api::Vulkan { const VARIANT: Backend = Backend::Vulkan; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance { @@ -1285,7 +1285,7 @@ impl HalApi for hal::api::Vulkan { } } -#[cfg(metal)] +#[cfg(feature = "metal")] impl HalApi for hal::api::Metal { const VARIANT: Backend = Backend::Metal; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance { @@ -1309,7 +1309,7 @@ impl HalApi for hal::api::Metal { } } -#[cfg(dx12)] +#[cfg(feature = "dx12")] impl HalApi for hal::api::Dx12 { const VARIANT: Backend = Backend::Dx12; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance { @@ -1333,7 +1333,7 @@ impl HalApi for hal::api::Dx12 { } } -#[cfg(dx11)] +#[cfg(feature = "dx11")] impl HalApi for hal::api::Dx11 { const VARIANT: Backend = Backend::Dx11; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance { @@ -1357,7 +1357,7 @@ impl HalApi for hal::api::Dx11 { } } -#[cfg(gl)] +#[cfg(feature = "gles")] impl HalApi for hal::api::Gles { const VARIANT: Backend = Backend::Gl; fn create_instance_from_hal(name: &str, hal_instance: Self::Instance) -> Instance { diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index f139409906..7cb8a4f1fd 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -54,15 +54,15 @@ fn downlevel_default_limits_less_than_default_limits() { pub struct Instance { #[allow(dead_code)] pub name: String, - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] pub vulkan: Option>, - #[cfg(metal)] + #[cfg(feature = "metal")] pub metal: Option>, - #[cfg(dx12)] + #[cfg(feature = "dx12")] pub dx12: Option>, - #[cfg(dx11)] + #[cfg(feature = "dx11")] pub dx11: Option>, - #[cfg(gl)] + #[cfg(feature = "gles")] pub gl: Option>, } @@ -87,15 +87,15 @@ impl Instance { Self { name: name.to_string(), - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: init(hal::api::Vulkan, backends), - #[cfg(metal)] + #[cfg(feature = "metal")] metal: init(hal::api::Metal, backends), - #[cfg(dx12)] + #[cfg(feature = "dx12")] dx12: init(hal::api::Dx12, backends), - #[cfg(dx11)] + #[cfg(feature = "dx11")] dx11: init(hal::api::Dx11, backends), - #[cfg(gl)] + #[cfg(feature = "gles")] gl: init(hal::api::Gles, backends), } } @@ -112,30 +112,30 @@ impl Instance { } } } - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] destroy(hal::api::Vulkan, &self.vulkan, surface.vulkan); - #[cfg(metal)] + #[cfg(feature = "metal")] destroy(hal::api::Metal, &self.metal, surface.metal); - #[cfg(dx12)] + #[cfg(feature = "dx12")] destroy(hal::api::Dx12, &self.dx12, surface.dx12); - #[cfg(dx11)] + #[cfg(feature = "dx11")] destroy(hal::api::Dx11, &self.dx11, surface.dx11); - #[cfg(gl)] + #[cfg(feature = "gles")] destroy(hal::api::Gles, &self.gl, surface.gl); } } pub struct Surface { pub(crate) presentation: Option, - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] pub vulkan: Option>, - #[cfg(metal)] + #[cfg(feature = "metal")] pub metal: Option>, - #[cfg(dx12)] + #[cfg(feature = "dx12")] pub dx12: Option>, - #[cfg(dx11)] + #[cfg(feature = "dx11")] pub dx11: Option>, - #[cfg(gl)] + #[cfg(feature = "gles")] pub gl: Option>, } @@ -451,15 +451,15 @@ impl Global { let surface = Surface { presentation: None, - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: init::(&self.instance.vulkan, display_handle, window_handle), - #[cfg(metal)] + #[cfg(feature = "metal")] metal: init::(&self.instance.metal, display_handle, window_handle), - #[cfg(dx12)] + #[cfg(feature = "dx12")] dx12: init::(&self.instance.dx12, display_handle, window_handle), - #[cfg(dx11)] + #[cfg(feature = "dx11")] dx11: init::(&self.instance.dx11, display_handle, window_handle), - #[cfg(gl)] + #[cfg(feature = "gles")] gl: init::(&self.instance.gl, display_handle, window_handle), }; @@ -468,7 +468,7 @@ impl Global { id.0 } - #[cfg(metal)] + #[cfg(feature = "metal")] pub fn instance_create_surface_metal( &self, layer: *mut std::ffi::c_void, @@ -486,9 +486,9 @@ impl Global { }, //acquired_texture: None, }), - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: None, - #[cfg(gl)] + #[cfg(feature = "gles")] gl: None, }; @@ -502,22 +502,26 @@ impl Global { &self, canvas: &web_sys::HtmlCanvasElement, id_in: Input, - ) -> SurfaceId { + ) -> Result { profiling::scope!("Instance::create_surface_webgl_canvas"); let surface = Surface { presentation: None, - gl: self.instance.gl.as_ref().map(|inst| HalSurface { - raw: { - inst.create_surface_from_canvas(canvas) - .expect("Create surface from canvas") - }, - }), + gl: self + .instance + .gl + .as_ref() + .map(|inst| { + Ok(HalSurface { + raw: inst.create_surface_from_canvas(canvas)?, + }) + }) + .transpose()?, }; let mut token = Token::root(); let id = self.surfaces.prepare(id_in).assign(surface, &mut token); - id.0 + Ok(id.0) } #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] @@ -525,25 +529,29 @@ impl Global { &self, canvas: &web_sys::OffscreenCanvas, id_in: Input, - ) -> SurfaceId { + ) -> Result { profiling::scope!("Instance::create_surface_webgl_offscreen_canvas"); let surface = Surface { presentation: None, - gl: self.instance.gl.as_ref().map(|inst| HalSurface { - raw: { - inst.create_surface_from_offscreen_canvas(canvas) - .expect("Create surface from offscreen canvas") - }, - }), + gl: self + .instance + .gl + .as_ref() + .map(|inst| { + Ok(HalSurface { + raw: inst.create_surface_from_offscreen_canvas(canvas)?, + }) + }) + .transpose()?, }; let mut token = Token::root(); let id = self.surfaces.prepare(id_in).assign(surface, &mut token); - id.0 + Ok(id.0) } - #[cfg(dx12)] + #[cfg(feature = "dx12")] /// # Safety /// /// The visual must be valid and able to be used to make a swapchain with. @@ -556,13 +564,13 @@ impl Global { let surface = Surface { presentation: None, - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] vulkan: None, dx12: self.instance.dx12.as_ref().map(|inst| HalSurface { raw: unsafe { inst.create_surface_from_visual(visual as _) }, }), dx11: None, - #[cfg(gl)] + #[cfg(feature = "gles")] gl: None, }; @@ -615,25 +623,25 @@ impl Global { let mut adapters = Vec::new(); - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] self.enumerate( hal::api::Vulkan, &self.instance.vulkan, &inputs, &mut adapters, ); - #[cfg(metal)] + #[cfg(feature = "metal")] self.enumerate( hal::api::Metal, &self.instance.metal, &inputs, &mut adapters, ); - #[cfg(dx12)] + #[cfg(feature = "dx12")] self.enumerate(hal::api::Dx12, &self.instance.dx12, &inputs, &mut adapters); - #[cfg(dx11)] + #[cfg(feature = "dx11")] self.enumerate(hal::api::Dx11, &self.instance.dx11, &inputs, &mut adapters); - #[cfg(gl)] + #[cfg(feature = "gles")] self.enumerate(hal::api::Gles, &self.instance.gl, &inputs, &mut adapters); adapters @@ -716,7 +724,7 @@ impl Global { .transpose()?; let mut device_types = Vec::new(); - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] let (id_vulkan, adapters_vk) = gather( hal::api::Vulkan, self.instance.vulkan.as_ref(), @@ -725,7 +733,7 @@ impl Global { desc.force_fallback_adapter, &mut device_types, ); - #[cfg(metal)] + #[cfg(feature = "metal")] let (id_metal, adapters_metal) = gather( hal::api::Metal, self.instance.metal.as_ref(), @@ -734,7 +742,7 @@ impl Global { desc.force_fallback_adapter, &mut device_types, ); - #[cfg(dx12)] + #[cfg(feature = "dx12")] let (id_dx12, adapters_dx12) = gather( hal::api::Dx12, self.instance.dx12.as_ref(), @@ -743,7 +751,7 @@ impl Global { desc.force_fallback_adapter, &mut device_types, ); - #[cfg(dx11)] + #[cfg(feature = "dx11")] let (id_dx11, adapters_dx11) = gather( hal::api::Dx11, self.instance.dx11.as_ref(), @@ -752,7 +760,7 @@ impl Global { desc.force_fallback_adapter, &mut device_types, ); - #[cfg(gl)] + #[cfg(feature = "gles")] let (id_gl, adapters_gl) = gather( hal::api::Gles, self.instance.gl.as_ref(), @@ -805,23 +813,23 @@ impl Global { }; let mut selected = preferred_gpu.unwrap_or(0); - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] if let Some(id) = self.select(&mut selected, id_vulkan, adapters_vk) { return Ok(id); } - #[cfg(metal)] + #[cfg(feature = "metal")] if let Some(id) = self.select(&mut selected, id_metal, adapters_metal) { return Ok(id); } - #[cfg(dx12)] + #[cfg(feature = "dx12")] if let Some(id) = self.select(&mut selected, id_dx12, adapters_dx12) { return Ok(id); } - #[cfg(dx11)] + #[cfg(feature = "dx11")] if let Some(id) = self.select(&mut selected, id_dx11, adapters_dx11) { return Ok(id); } - #[cfg(gl)] + #[cfg(feature = "gles")] if let Some(id) = self.select(&mut selected, id_gl, adapters_gl) { return Ok(id); } @@ -845,15 +853,15 @@ impl Global { let fid = A::hub(self).adapters.prepare(input); match A::VARIANT { - #[cfg(vulkan)] + #[cfg(feature = "vulkan")] Backend::Vulkan => fid.assign(Adapter::new(hal_adapter), &mut token).0, - #[cfg(metal)] + #[cfg(feature = "metal")] Backend::Metal => fid.assign(Adapter::new(hal_adapter), &mut token).0, - #[cfg(dx12)] + #[cfg(feature = "dx12")] Backend::Dx12 => fid.assign(Adapter::new(hal_adapter), &mut token).0, - #[cfg(dx11)] + #[cfg(feature = "dx11")] Backend::Dx11 => fid.assign(Adapter::new(hal_adapter), &mut token).0, - #[cfg(gl)] + #[cfg(feature = "gles")] Backend::Gl => fid.assign(Adapter::new(hal_adapter), &mut token).0, _ => unreachable!(), } diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index ce0f7f5087..c8b3fb7296 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -243,6 +243,91 @@ If you are running this program on native and not in a browser and wish to work Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \ platform supports."; +// #[cfg] attributes in exported macros are interesting! +// +// The #[cfg] conditions in a macro's expansion are evaluated using the +// configuration options (features, target architecture and os, etc.) in force +// where the macro is *used*, not where it is *defined*. That is, if crate A +// defines a macro like this: +// +// #[macro_export] +// macro_rules! if_bleep { +// { } => { +// #[cfg(feature = "bleep")] +// bleep(); +// } +// } +// +// and then crate B uses it like this: +// +// fn f() { +// if_bleep! { } +// } +// +// then it is crate B's `"bleep"` feature, not crate A's, that determines +// whether the macro expands to a function call or an empty statement. The +// entire configuration predicate is evaluated in the use's context, not the +// definition's. +// +// Since `wgpu-core` selects back ends using features, we need to make sure the +// arms of the `gfx_select!` macro are pruned according to `wgpu-core`'s +// features, not those of whatever crate happens to be using `gfx_select!`. This +// means we can't use `#[cfg]` attributes in `gfx_select!`s definition itself. +// Instead, for each backend, `gfx_select!` must use a macro whose definition is +// selected by `#[cfg]` in `wgpu-core`. The configuration predicate is still +// evaluated when the macro is used; we've just moved the `#[cfg]` into a macro +// used by `wgpu-core` itself. + +/// Define an exported macro named `$public` that expands to an expression if +/// the feature `$feature` is enabled, or to a panic otherwise. +/// +/// For a call like this: +/// +/// define_backend_caller! { name, hidden_name, feature } +/// +/// define a macro `name`, used like this: +/// +/// name!(expr) +/// +/// that expands to `expr` if `feature` is enabled, or a panic otherwise. +/// +/// Because of odd technical limitations on exporting macros expanded by other +/// macros, you must supply both a public-facing name for the macro and a +/// private name, which is never used outside this macro. For details: +/// +macro_rules! define_backend_caller { + { $public:ident, $private:ident if $feature:literal } => { + #[cfg(feature = $feature )] + #[macro_export] + macro_rules! $private { + ( $call:expr ) => ( $call ) + } + + #[cfg(not(feature = $feature ))] + #[macro_export] + macro_rules! $private { + ( $call:expr ) => ( + panic!("Identifier refers to disabled backend feature {:?}", $feature) + ) + } + + // See note about rust-lang#52234 above. + #[doc(hidden)] pub use $private as $public; + } +} + +// Define a macro for each `gfx_select!` match arm. For example, +// +// gfx_if_vulkan!(expr) +// +// expands to `expr` if the `"vulkan"` feature is enabled, or to a panic +// otherwise. +define_backend_caller! { gfx_if_vulkan, gfx_if_vulkan_hidden if "vulkan" } +define_backend_caller! { gfx_if_metal, gfx_if_metal_hidden if "metal" } +define_backend_caller! { gfx_if_dx12, gfx_if_dx12_hidden if "dx12" } +define_backend_caller! { gfx_if_dx11, gfx_if_dx11_hidden if "dx11" } +define_backend_caller! { gfx_if_gles, gfx_if_gles_hidden if "gles" } + /// Dispatch on an [`Id`]'s backend to a backend-generic method. /// /// Uses of this macro have the form: @@ -291,29 +376,13 @@ platform supports."; #[macro_export] macro_rules! gfx_select { ($id:expr => $global:ident.$method:ident( $($param:expr),* )) => { - // Note: For some reason the cfg aliases defined in build.rs - // don't succesfully apply in this macro so we must specify - // their equivalents manually. match $id.backend() { - #[cfg(any( - all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")), - feature = "vulkan-portability" - ))] - wgt::Backend::Vulkan => $global.$method::<$crate::api::Vulkan>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))] - wgt::Backend::Metal => $global.$method::<$crate::api::Metal>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgt::Backend::Dx12 => $global.$method::<$crate::api::Dx12>( $($param),* ), - #[cfg(all(not(target_arch = "wasm32"), windows))] - wgt::Backend::Dx11 => $global.$method::<$crate::api::Dx11>( $($param),* ), - #[cfg(any( - all(unix, not(target_os = "macos"), not(target_os = "ios")), - feature = "angle", - target_arch = "wasm32" - ))] - wgt::Backend::Gl => $global.$method::<$crate::api::Gles>( $($param),+ ), + wgt::Backend::Vulkan => $crate::gfx_if_vulkan!($global.$method::<$crate::api::Vulkan>( $($param),* )), + wgt::Backend::Metal => $crate::gfx_if_metal!($global.$method::<$crate::api::Metal>( $($param),* )), + wgt::Backend::Dx12 => $crate::gfx_if_dx12!($global.$method::<$crate::api::Dx12>( $($param),* )), + wgt::Backend::Dx11 => $crate::gfx_if_dx11!($global.$method::<$crate::api::Dx11>( $($param),* )), + wgt::Backend::Gl => $crate::gfx_if_gles!($global.$method::<$crate::api::Gles>( $($param),+ )), other => panic!("Unexpected backend {:?}", other), - } }; } diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 4302df7e61..e59ec0aaa1 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -551,9 +551,9 @@ pub struct TextureViewDescriptor<'a> { pub format: Option, /// The dimension of the texture view. /// - /// - For 1D textures, this must be `1D`. + /// - For 1D textures, this must be `D1`. /// - For 2D textures it must be one of `D2`, `D2Array`, `Cube`, or `CubeArray`. - /// - For 3D textures it must be `3D`. + /// - For 3D textures it must be `D3`. pub dimension: Option, /// Range within the texture that is accessible via this view. pub range: wgt::ImageSubresourceRange, diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml index 2f79c2e51e..2d333930c6 100644 --- a/wgpu-hal/Cargo.toml +++ b/wgpu-hal/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wgpu-hal" -version.workspace = true -authors.workspace = true -edition.workspace = true +version = "0.14.0" +authors = ["wgpu developers"] +edition = "2021" description = "WebGPU hardware abstraction layer" -homepage.workspace = true -repository.workspace = true -keywords.workspace = true -license.workspace = true +homepage = "https://wgpu.rs/" +repository = "https://github.com/gfx-rs/wgpu" +keywords = ["graphics"] +license = "MIT OR Apache-2.0" # Override the workspace's `rust-version` key. Firefox uses `cargo vendor` to # copy the crates it actually uses out of the workspace, so it's meaningful for @@ -27,7 +27,7 @@ rustdoc-args = ["--cfg", "docsrs"] [lib] [features] -default = ["gles"] +default = [] metal = ["naga/msl-out", "block", "foreign-types"] vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "smallvec"] gles = ["naga/glsl-out", "glow", "egl", "libloading"] @@ -44,79 +44,84 @@ name = "raw-gles" required-features = ["gles"] [dependencies] -bitflags.workspace = true -parking_lot.workspace = true -profiling.workspace = true -raw-window-handle.workspace = true -thiserror.workspace = true +bitflags = "1" +parking_lot = ">=0.11,<0.13" +profiling = { version = "1", default-features = false } +raw-window-handle = "0.5" +thiserror = "1" # backends common -arrayvec.workspace = true -fxhash.workspace = true -log.workspace = true -renderdoc-sys = { workspace = true, optional = true } +arrayvec = "0.7" +fxhash = "0.2.1" +log = "0.4" +renderdoc-sys = { version = "0.7.1", optional = true } # backend: Metal -block = { workspace = true, optional = true } -foreign-types = { workspace = true, optional = true } +block = { version = "0.1", optional = true } +foreign-types = { version = "0.3", optional = true } # backend: Vulkan -ash = { workspace = true, optional = true } -gpu-alloc = { workspace = true, optional = true } -gpu-descriptor = { workspace = true, optional = true } -smallvec = { workspace = true, optional = true, features = ["union"] } +ash = { version = "0.37.1", optional = true } +gpu-alloc = { version = "0.5", optional = true } +gpu-descriptor = { version = "0.2", optional = true } +smallvec = { version = "1", optional = true, features = ["union"] } # backend: Gles -glow = { workspace = true, optional = true } +glow = { git = "https://github.com/grovesNL/glow", rev = "c8a011fcd57a5c68cc917ed394baa484bdefc909", optional = true } # backend: Dx12 -bit-set = { workspace = true, optional = true } -range-alloc = { workspace = true, optional = true } +bit-set = { version = "0.5", optional = true } +range-alloc = { version = "0.1", optional = true } [dependencies.wgt] -workspace = true +package = "wgpu-types" +path = "../wgpu-types" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] -egl = { workspace = true, features = ["dynamic"], optional = true } -libloading = { workspace = true, optional = true } +egl = { package = "khronos-egl", version = "4.1", features = ["dynamic"], optional = true } +libloading = { version = "0.7", optional = true } [target.'cfg(target_os = "emscripten")'.dependencies] -egl = { workspace = true, features = ["static", "no-pkg-config"] } +egl = { package = "khronos-egl", version = "4.1", features = ["static", "no-pkg-config"] } #Note: it's unused by emscripten, but we keep it to have single code base in egl.rs -libloading = { workspace = true, optional = true } +libloading = { version = "0.7", optional = true } [target.'cfg(windows)'.dependencies] -winapi = { workspace = true, features = ["profileapi", "libloaderapi", "windef", "winuser", "dcomp"] } -native = { workspace = true, features = ["libloading"], optional = true } +winapi = { version = "0.3", features = ["profileapi", "libloaderapi", "windef", "winuser", "dcomp"] } +native = { package = "d3d12", version = "0.5.0", features = ["libloading"], optional = true } [target.'cfg(any(target_os="macos", target_os="ios"))'.dependencies] -mtl.workspace = true -objc.workspace = true -core-graphics-types.workspace = true +mtl = { package = "metal", version = "0.24.0" } +objc = "0.2.5" +core-graphics-types = "0.1" [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] -wasm-bindgen.workspace = true -web-sys = { workspace = true, features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext", "OffscreenCanvas"] } -js-sys.workspace = true +wasm-bindgen = "0.2.83" +web-sys = { version = "0.3.60", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext", "OffscreenCanvas"] } +js-sys = "0.3.60" [target.'cfg(unix)'.dependencies] -libc.workspace = true +libc = "0.2" [target.'cfg(target_os = "android")'.dependencies] -android_system_properties.workspace = true +android_system_properties = "0.1.1" [dependencies.naga] -workspace = true +git = "https://github.com/gfx-rs/naga" +rev = "e7fc8e6" +version = "0.10" features = ["clone"] # DEV dependencies [dev-dependencies.naga] -workspace = true +git = "https://github.com/gfx-rs/naga" +rev = "e7fc8e6" +version = "0.10" features = ["wgsl-in"] [dev-dependencies] -env_logger.workspace = true -winit.workspace = true # for "halmark" example +env_logger = "0.9" +winit = "0.27.1" # for "halmark" example [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] -glutin.workspace = true # for "gles" example +glutin = "0.29.1" # for "gles" example diff --git a/wgpu-hal/examples/halmark/shader.wgsl b/wgpu-hal/examples/halmark/shader.wgsl index 60aac686b8..ffa7264591 100644 --- a/wgpu-hal/examples/halmark/shader.wgsl +++ b/wgpu-hal/examples/halmark/shader.wgsl @@ -1,12 +1,17 @@ struct Globals { mvp: mat4x4, size: vec2, + _pad0: u32, + _pad1: u32, }; struct Locals { position: vec2, velocity: vec2, color: u32, + _pad0: u32, + _pad1: u32, + _pad2: u32, }; @group(0) diff --git a/wgpu-hal/src/dx11/adapter.rs b/wgpu-hal/src/dx11/adapter.rs index a040e087cb..19de44ac8e 100644 --- a/wgpu-hal/src/dx11/adapter.rs +++ b/wgpu-hal/src/dx11/adapter.rs @@ -127,6 +127,7 @@ impl super::Adapter { features |= wgt::Features::DEPTH_CLIP_CONTROL; features |= wgt::Features::TIMESTAMP_QUERY; features |= wgt::Features::PIPELINE_STATISTICS_QUERY; + features |= wgt::Features::SHADER_PRIMITIVE_INDEX; } if feature_level >= FL10_1 { diff --git a/wgpu-hal/src/dx12/adapter.rs b/wgpu-hal/src/dx12/adapter.rs index 57b238c9f8..2119ebd522 100644 --- a/wgpu-hal/src/dx12/adapter.rs +++ b/wgpu-hal/src/dx12/adapter.rs @@ -209,7 +209,8 @@ impl super::Adapter { | wgt::Features::TEXTURE_COMPRESSION_BC | wgt::Features::CLEAR_TEXTURE | wgt::Features::TEXTURE_FORMAT_16BIT_NORM - | wgt::Features::PUSH_CONSTANTS; + | wgt::Features::PUSH_CONSTANTS + | wgt::Features::SHADER_PRIMITIVE_INDEX; //TODO: in order to expose this, we need to run a compute shader // that extract the necessary statistics out of the D3D12 result. // Alternatively, we could allocate a buffer for the query set, diff --git a/wgpu-hal/src/gles/adapter.rs b/wgpu-hal/src/gles/adapter.rs index aa7d1f002c..3794909ea5 100644 --- a/wgpu-hal/src/gles/adapter.rs +++ b/wgpu-hal/src/gles/adapter.rs @@ -338,6 +338,10 @@ impl super::Adapter { wgt::Features::MULTIVIEW, extensions.contains("OVR_multiview2"), ); + features.set( + wgt::Features::SHADER_PRIMITIVE_INDEX, + ver >= (3, 2) || extensions.contains("OES_geometry_shader"), + ); let gles_bcn_exts = [ "GL_EXT_texture_compression_s3tc_srgb", "GL_EXT_texture_compression_rgtc", @@ -421,6 +425,10 @@ impl super::Adapter { super::PrivateCapabilities::COLOR_BUFFER_FLOAT, color_buffer_float, ); + private_caps.set( + super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR, + extensions.contains("OES_texture_float_linear"), + ); let max_texture_size = unsafe { gl.get_parameter_i32(glow::MAX_TEXTURE_SIZE) } as u32; let max_texture_3d_size = unsafe { gl.get_parameter_i32(glow::MAX_3D_TEXTURE_SIZE) } as u32; @@ -730,6 +738,9 @@ impl crate::Adapter for super::Adapter { | Tfc::MULTISAMPLE_RESOLVE, ); + let texture_float_linear = + private_caps_fn(super::PrivateCapabilities::TEXTURE_FLOAT_LINEAR, filterable); + match format { Tf::R8Unorm => filterable_renderable, Tf::R8Snorm => filterable, @@ -746,7 +757,7 @@ impl crate::Adapter for super::Adapter { Tf::Rg8Sint => renderable, Tf::R32Uint => renderable | storage, Tf::R32Sint => renderable | storage, - Tf::R32Float => unfilterable | storage | float_renderable, + Tf::R32Float => unfilterable | storage | float_renderable | texture_float_linear, Tf::Rg16Uint => renderable, Tf::Rg16Sint => renderable, Tf::Rg16Unorm => empty, @@ -761,7 +772,7 @@ impl crate::Adapter for super::Adapter { Tf::Rg11b10Float => filterable | float_renderable, Tf::Rg32Uint => renderable, Tf::Rg32Sint => renderable, - Tf::Rg32Float => unfilterable | float_renderable, + Tf::Rg32Float => unfilterable | float_renderable | texture_float_linear, Tf::Rgba16Uint => renderable | storage, Tf::Rgba16Sint => renderable | storage, Tf::Rgba16Unorm => empty, @@ -769,7 +780,7 @@ impl crate::Adapter for super::Adapter { Tf::Rgba16Float => filterable | storage | half_float_renderable, Tf::Rgba32Uint => renderable | storage, Tf::Rgba32Sint => renderable | storage, - Tf::Rgba32Float => unfilterable | storage | float_renderable, + Tf::Rgba32Float => unfilterable | storage | float_renderable | texture_float_linear, //Tf::Stencil8 | Tf::Depth16Unorm | Tf::Depth32Float diff --git a/wgpu-hal/src/gles/mod.rs b/wgpu-hal/src/gles/mod.rs index bad5c08b91..e57b05a979 100644 --- a/wgpu-hal/src/gles/mod.rs +++ b/wgpu-hal/src/gles/mod.rs @@ -148,6 +148,8 @@ bitflags::bitflags! { const COLOR_BUFFER_HALF_FLOAT = 1 << 8; /// Supports `f11/f10` and `f32` color buffers const COLOR_BUFFER_FLOAT = 1 << 9; + /// Supports linear flitering `f32` textures. + const TEXTURE_FLOAT_LINEAR = 1 << 10; } } diff --git a/wgpu-hal/src/gles/web.rs b/wgpu-hal/src/gles/web.rs index b9e7302182..091c494ddc 100644 --- a/wgpu-hal/src/gles/web.rs +++ b/wgpu-hal/src/gles/web.rs @@ -33,32 +33,53 @@ impl Instance { &self, canvas: &web_sys::HtmlCanvasElement, ) -> Result { - let webgl2_context = canvas - .get_context_with_context_options("webgl2", &Self::create_context_options()) - .expect("Cannot create WebGL2 context") - .and_then(|context| context.dyn_into::().ok()) - .expect("Cannot convert into WebGL2 context"); - - *self.webgl2_context.lock() = Some(webgl2_context.clone()); - - Ok(Surface { - webgl2_context, - srgb_present_program: None, - swapchain: None, - texture: None, - presentable: true, - }) + self.create_surface_from_context( + canvas.get_context_with_context_options("webgl2", &Self::create_context_options()), + ) } pub fn create_surface_from_offscreen_canvas( &self, canvas: &web_sys::OffscreenCanvas, ) -> Result { - let webgl2_context = canvas - .get_context_with_context_options("webgl2", &Self::create_context_options()) - .expect("Cannot create WebGL2 context") - .and_then(|context| context.dyn_into::().ok()) - .expect("Cannot convert into WebGL2 context"); + self.create_surface_from_context( + canvas.get_context_with_context_options("webgl2", &Self::create_context_options()), + ) + } + + /// Common portion of public `create_surface_from_*` functions. + /// + /// Note: Analogous code also exists in the WebGPU backend at + /// `wgpu::backend::web::Context`. + fn create_surface_from_context( + &self, + context_result: Result, wasm_bindgen::JsValue>, + ) -> Result { + let context_object: js_sys::Object = match context_result { + Ok(Some(context)) => context, + Ok(None) => { + // + // A getContext() call “returns null if contextId is not supported, or if the + // canvas has already been initialized with another context type”. Additionally, + // “not supported” could include “insufficient GPU resources” or “the GPU process + // previously crashed”. So, we must return it as an `Err` since it could occur + // for circumstances outside the application author's control. + return Err(crate::InstanceError); + } + Err(js_error) => { + // + // A thrown exception indicates misuse of the canvas state. Ideally we wouldn't + // panic in this case, but for now, `InstanceError` conveys no detail, so it + // is more informative to panic with a specific message. + panic!("canvas.getContext() threw {js_error:?}") + } + }; + + // Not returning this error because it is a type error that shouldn't happen unless + // the browser, JS builtin objects, or wasm bindings are misbehaving somehow. + let webgl2_context: web_sys::WebGl2RenderingContext = context_object + .dyn_into() + .expect("canvas context is not a WebGl2RenderingContext"); *self.webgl2_context.lock() = Some(webgl2_context.clone()); diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs index f9ce21e122..9a82b54a25 100644 --- a/wgpu-hal/src/lib.rs +++ b/wgpu-hal/src/lib.rs @@ -49,6 +49,15 @@ clippy::pattern_type_mismatch, )] +#[cfg(not(any( + feature = "dx11", + feature = "dx12", + feature = "gles", + feature = "metal", + feature = "vulkan" +)))] +compile_error!("No back ends enabled in `wgpu-hal`. Enable at least one backend feature."); + #[cfg(all(feature = "metal", not(any(target_os = "macos", target_os = "ios"))))] compile_error!("Metal API enabled on non-Apple OS. If your project is not using resolver=\"2\" in Cargo.toml, it should."); #[cfg(all(feature = "dx12", not(windows)))] diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs index 63264a70d8..5ade092c24 100644 --- a/wgpu-hal/src/metal/adapter.rs +++ b/wgpu-hal/src/metal/adapter.rs @@ -732,6 +732,8 @@ impl super::PrivateCapabilities { supports_depth_clip_control: os_is_mac || device.supports_feature_set(MTLFeatureSet::iOS_GPUFamily4_v1), supports_preserve_invariance: version.at_least((11, 0), (13, 0)), + // Metal 2.2 on mac, 2.3 on iOS. + supports_shader_primitive_index: version.at_least((10, 15), (14, 0)), has_unified_memory: if version.at_least((10, 15), (13, 0)) { Some(device.has_unified_memory()) } else { @@ -770,6 +772,10 @@ impl super::PrivateCapabilities { features.set(F::TEXTURE_COMPRESSION_ETC2, self.format_eac_etc); features.set(F::DEPTH_CLIP_CONTROL, self.supports_depth_clip_control); + features.set( + F::SHADER_PRIMITIVE_INDEX, + self.supports_shader_primitive_index, + ); features.set( F::TEXTURE_BINDING_ARRAY diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs index c0b0b5d320..37f101cff7 100644 --- a/wgpu-hal/src/metal/mod.rs +++ b/wgpu-hal/src/metal/mod.rs @@ -231,6 +231,7 @@ struct PrivateCapabilities { supports_mutability: bool, supports_depth_clip_control: bool, supports_preserve_invariance: bool, + supports_shader_primitive_index: bool, has_unified_memory: Option, } diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs index 084be72de9..dafd15f500 100644 --- a/wgpu-hal/src/vulkan/device.rs +++ b/wgpu-hal/src/vulkan/device.rs @@ -52,7 +52,7 @@ impl super::DeviceShared { let name = unsafe { CStr::from_bytes_with_nul_unchecked(name_bytes) }; let _result = unsafe { - extension.debug_utils_set_object_name( + extension.set_debug_utils_object_name( self.raw.handle(), &vk::DebugUtilsObjectNameInfoEXT::builder() .object_type(object_type) diff --git a/wgpu-types/Cargo.toml b/wgpu-types/Cargo.toml index 0748296a81..4b608b472f 100644 --- a/wgpu-types/Cargo.toml +++ b/wgpu-types/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "wgpu-types" -version.workspace = true -authors.workspace = true -edition.workspace = true +version = "0.14.0" +authors = ["wgpu developers"] +edition = "2021" description = "WebGPU types" -homepage.workspace = true -repository.workspace = true -keywords.workspace = true -license.workspace = true +homepage = "https://wgpu.rs/" +repository = "https://github.com/gfx-rs/wgpu" +keywords = ["graphics"] +license = "MIT OR Apache-2.0" [package.metadata.docs.rs] all-features = true @@ -20,8 +20,8 @@ trace = ["serde"] replay = ["serde"] [dependencies] -bitflags.workspace = true -serde = { workspace = true, features = ["serde_derive"], optional = true } +bitflags = "1" +serde = { version = "1", features = ["serde_derive"], optional = true } [dev-dependencies] -serde_json.workspace = true +serde_json = "1.0.85" diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index f333d2d603..573a5771fd 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -661,7 +661,7 @@ bitflags::bitflags! { /// /// This is a native-only feature. const TEXTURE_COMPRESSION_ASTC_HDR = 1 << 40; - /// Allows for timestamp queries inside renderpasses. Metal does not allow this + /// Allows for timestamp queries inside render passes. Metal does not allow this /// on Apple GPUs. /// /// Implies [`Features::TIMESTAMP_QUERIES`] is supported. @@ -698,9 +698,9 @@ impl Features { /// - [`Limits::downlevel_webgl2_defaults()`] This is a set of limits that is lower even than the /// [`downlevel_defaults()`], configured to be low enough to support running in the browser using /// WebGL2. -/// - [`Limits::default()`]. This is the set of limits that is guarenteed to work on all modern -/// backends and is guarenteed to be supported by WebGPU. Applications needing more modern -/// features can use this as a reasonable set of limits if they are targetting only desktop and +/// - [`Limits::default()`]. This is the set of limits that is guaranteed to work on all modern +/// backends and is guaranteed to be supported by WebGPU. Applications needing more modern +/// features can use this as a reasonable set of limits if they are targeting only desktop and /// modern mobile devices. /// /// We recommend starting with the most restrictive limits you can and manually increasing the @@ -813,7 +813,7 @@ pub struct Limits { pub max_compute_workgroups_per_dimension: u32, /// A limit above which buffer allocations are guaranteed to fail. /// - /// Buffer allocations below the maximum buffer size may not succed depending on available memory, + /// Buffer allocations below the maximum buffer size may not succeed depending on available memory, /// fragmentation and other factors. pub max_buffer_size: u64, } @@ -849,13 +849,13 @@ impl Default for Limits { max_compute_workgroup_size_y: 256, max_compute_workgroup_size_z: 64, max_compute_workgroups_per_dimension: 65535, - max_buffer_size: 1 << 30, + max_buffer_size: 1 << 28, } } } impl Limits { - /// These default limits are guarenteed to be compatible with GLES-3.1, and D3D11 + /// These default limits are guaranteed to be compatible with GLES-3.1, and D3D11 pub fn downlevel_defaults() -> Self { Self { max_texture_dimension_1d: 2048, @@ -890,7 +890,7 @@ impl Limits { } } - /// These default limits are guarenteed to be compatible with GLES-3.0, and D3D11, and WebGL2 + /// These default limits are guaranteed to be compatible with GLES-3.0, and D3D11, and WebGL2 pub fn downlevel_webgl2_defaults() -> Self { Self { max_uniform_buffers_per_shader_stage: 11, @@ -1218,7 +1218,7 @@ pub struct DeviceDescriptor { } impl DeviceDescriptor { - /// + /// Takes a closure and maps the label of the device descriptor into another. pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> DeviceDescriptor { DeviceDescriptor { label: fun(&self.label), @@ -2086,7 +2086,7 @@ pub enum TextureFormat { Astc { /// compressed block dimensions block: AstcBlock, - /// + /// ASTC RGBA channel channel: AstcChannel, }, } @@ -3707,7 +3707,7 @@ pub struct BufferDescriptor { } impl BufferDescriptor { - /// + /// Takes a closure and maps the label of the buffer descriptor into another. pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> BufferDescriptor { BufferDescriptor { label: fun(&self.label), @@ -3732,7 +3732,7 @@ pub struct CommandEncoderDescriptor { } impl CommandEncoderDescriptor { - /// + /// Takes a closure and maps the label of the command encoder descriptor into another. pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> CommandEncoderDescriptor { CommandEncoderDescriptor { label: fun(&self.label), @@ -3876,7 +3876,7 @@ bitflags::bitflags! { const TEXTURE_BINDING = 1 << 2; /// Allows a texture to be a [`BindingType::StorageTexture`] in a bind group. const STORAGE_BINDING = 1 << 3; - /// Allows a texture to be an output attachment of a renderpass. + /// Allows a texture to be an output attachment of a render pass. const RENDER_ATTACHMENT = 1 << 4; } } @@ -3998,13 +3998,13 @@ impl PresentationTimestamp { #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct Color { - /// + /// Red component of the color pub r: f64, - /// + /// Green component of the color pub g: f64, - /// + /// Blue component of the color pub b: f64, - /// + /// Alpha component of the color pub a: f64, } @@ -4078,11 +4078,11 @@ pub enum TextureDimension { #[cfg_attr(feature = "replay", derive(Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct Origin3d { - /// + /// X position of the origin pub x: u32, - /// + /// Y position of the origin pub y: u32, - /// + /// Z position of the origin pub z: u32, } @@ -4107,11 +4107,11 @@ impl Default for Origin3d { #[cfg_attr(feature = "replay", derive(Deserialize))] #[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))] pub struct Extent3d { - /// + /// Width of the extent pub width: u32, - /// + /// Height of the extent pub height: u32, - /// + /// The depth of the extent or the number of array layers #[cfg_attr(feature = "serde", serde(default = "default_depth"))] pub depth_or_array_layers: u32, } @@ -4316,7 +4316,7 @@ pub struct TextureDescriptor { } impl TextureDescriptor { - /// + /// Takes a closure and maps the label of the texture descriptor into another. pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> TextureDescriptor { TextureDescriptor { label: fun(&self.label), @@ -4478,7 +4478,7 @@ pub struct CommandBufferDescriptor { } impl CommandBufferDescriptor { - /// + /// Takes a closure and maps the label of the command buffer descriptor into another. pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> CommandBufferDescriptor { CommandBufferDescriptor { label: fun(&self.label), @@ -4517,7 +4517,7 @@ pub struct RenderBundleDescriptor { } impl RenderBundleDescriptor { - /// + /// Takes a closure and maps the label of the render bundle descriptor into another. pub fn map_label(&self, fun: impl FnOnce(&L) -> K) -> RenderBundleDescriptor { RenderBundleDescriptor { label: fun(&self.label), @@ -5069,7 +5069,7 @@ pub struct QuerySetDescriptor { } impl QuerySetDescriptor { - /// + /// Takes a closure and maps the label of the query set descriptor into another. pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> QuerySetDescriptor { QuerySetDescriptor { label: fun(&self.label), diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 36272c0fda..bb446b7d8e 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -83,25 +83,62 @@ wgsl = ["wgc?/wgsl"] trace = ["serde", "wgc/trace"] replay = ["serde", "wgc/replay"] angle = ["wgc/angle"] -webgl = ["wgc"] +webgl = ["hal", "wgc"] emscripten = ["webgl"] vulkan-portability = ["wgc/vulkan-portability"] expose-ids = [] -[target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc] +# wgpu-core is always available as an optional dependency, "wgc". +# Whenever wgpu-core is selected, we want the GLES backend and raw +# window handle support. +[dependencies.wgc] +optional = true workspace = true -features = ["raw-window-handle"] +features = ["raw-window-handle", "gles"] -[target.'cfg(target_arch = "wasm32")'.dependencies.wgc] +# wgpu-core is required whenever not targeting web APIs directly. +# Whenever wgpu-core is selected, we want the GLES backend and raw +# window handle support. +[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies.wgc] workspace = true -features = ["raw-window-handle"] -optional = true +features = ["raw-window-handle", "gles"] + +# We want the wgpu-core Metal backend on macOS and iOS. +# (We should consider also enabling "vulkan" for Vulkan Portability.) +[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc] +workspace = true +features = ["metal"] + +# We want the wgpu-core Direct3D backends on Windows. +[target.'cfg(windows)'.dependencies.wgc] +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] +workspace = true +features = ["vulkan"] + +[target.'cfg(target_os = "emscripten")'.dependencies.wgc] +workspace = true +features = ["emscripten"] [dependencies.wgt] workspace = true -[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies.hal] +# We need wgpu-hal unless we're targeting the web APIs. +[target.'cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))'.dependencies] +hal = { workspace = true } + +[target.'cfg(all(not(target_arch = "wasm32"), unix, not(target_os = "ios"), not(target_os = "macos")))'.dependencies] +hal = { workspace = true, features = ["renderdoc"] } + +[target.'cfg(windows)'.dependencies] +hal = { workspace = true, features = ["renderdoc"] } + +[target.'cfg(target_arch = "wasm32")'.dependencies.hal] workspace = true +optional = true [dependencies] arrayvec.workspace = true @@ -126,7 +163,8 @@ obj.workspace = true pollster.workspace = true png.workspace = true nanorand = { workspace = true, features = ["wyrand"] } -winit.workspace = true # for "halmark" example # for "halmark" example +wasm-bindgen-test.workspace = true +winit.workspace = true # for "halmark" example [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] async-executor.workspace = true @@ -283,12 +321,13 @@ parking_lot.workspace = true [target.'cfg(target_arch = "wasm32")'.dev-dependencies] console_error_panic_hook.workspace = true console_log.workspace = true -# We need these features in the framework examples +# We need these features in the framework examples and tests web-sys = { workspace = true, features = [ "Location", "Blob", "RequestInit", "RequestMode", "Request", - "Response" + "Response", + "WebGl2RenderingContext" ] } diff --git a/wgpu/examples/framework.rs b/wgpu/examples/framework.rs index 122c88170d..9af044bf66 100644 --- a/wgpu/examples/framework.rs +++ b/wgpu/examples/framework.rs @@ -164,7 +164,7 @@ async fn setup(title: &str) -> Setup { let size = window.inner_size(); #[cfg(any(not(target_arch = "wasm32"), target_os = "emscripten"))] - let surface = instance.create_surface(&window); + let surface = instance.create_surface(&window).unwrap(); #[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))] let surface = { if let Some(offscreen_canvas_setup) = &offscreen_canvas_setup { @@ -174,7 +174,8 @@ async fn setup(title: &str) -> Setup { } else { instance.create_surface(&window) } - }; + } + .unwrap(); (size, surface) }; diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 4c19babf3c..58e7ba1a90 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -8,7 +8,7 @@ use winit::{ async fn run(event_loop: EventLoop<()>, window: Window) { let size = window.inner_size(); let instance = wgpu::Instance::new(wgpu::Backends::all()); - let surface = unsafe { instance.create_surface(&window) }; + let surface = unsafe { instance.create_surface(&window) }.unwrap(); let adapter = instance .request_adapter(&wgpu::RequestAdapterOptions { power_preference: wgpu::PowerPreference::default(), diff --git a/wgpu/examples/hello-windows/main.rs b/wgpu/examples/hello-windows/main.rs index 0dcc87daac..4a93cf6b7f 100644 --- a/wgpu/examples/hello-windows/main.rs +++ b/wgpu/examples/hello-windows/main.rs @@ -20,7 +20,7 @@ struct Viewport { impl ViewportDesc { fn new(window: Window, background: wgpu::Color, instance: &wgpu::Instance) -> Self { - let surface = unsafe { instance.create_surface(&window) }; + let surface = unsafe { instance.create_surface(&window) }.unwrap(); Self { window, background, diff --git a/wgpu/examples/mipmap/blit.wgsl b/wgpu/examples/mipmap/blit.wgsl index c293e51c73..69adbb3ccd 100644 --- a/wgpu/examples/mipmap/blit.wgsl +++ b/wgpu/examples/mipmap/blit.wgsl @@ -3,6 +3,24 @@ struct VertexOutput { @location(0) tex_coords: vec2, }; +// meant to be called with 3 vertex indices: 0, 1, 2 +// draws one large triangle over the clip space like this: +// (the asterisks represent the clip space bounds) +//-1,1 1,1 +// --------------------------------- +// | * . +// | * . +// | * . +// | * . +// | * . +// | * . +// |*************** +// | . 1,-1 +// | . +// | . +// | . +// | . +// |. @vertex fn vs_main(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { var result: VertexOutput; diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index f6ce4e343c..37506bf19a 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -219,24 +219,30 @@ impl Context { pub fn instance_create_surface_from_canvas( self: &Arc, canvas: &web_sys::HtmlCanvasElement, - ) -> Surface { - let id = self.0.create_surface_webgl_canvas(canvas, ()); - Surface { + ) -> Result { + let id = self + .0 + .create_surface_webgl_canvas(canvas, ()) + .map_err(|hal::InstanceError| crate::CreateSurfaceError {})?; + Ok(Surface { id, configured_device: Mutex::default(), - } + }) } #[cfg(all(target_arch = "wasm32", feature = "webgl", not(feature = "emscripten")))] pub fn instance_create_surface_from_offscreen_canvas( self: &Arc, canvas: &web_sys::OffscreenCanvas, - ) -> Surface { - let id = self.0.create_surface_webgl_offscreen_canvas(canvas, ()); - Surface { + ) -> Result { + let id = self + .0 + .create_surface_webgl_offscreen_canvas(canvas, ()) + .map_err(|hal::InstanceError| crate::CreateSurfaceError {})?; + Ok(Surface { id, configured_device: Mutex::default(), - } + }) } #[cfg(target_os = "windows")] @@ -943,13 +949,13 @@ impl crate::Context for Context { &self, display_handle: raw_window_handle::RawDisplayHandle, window_handle: raw_window_handle::RawWindowHandle, - ) -> Self::SurfaceId { - Surface { + ) -> Result { + Ok(Surface { id: self .0 .instance_create_surface(display_handle, window_handle, ()), configured_device: Mutex::new(None), - } + }) } fn instance_request_adapter( diff --git a/wgpu/src/backend/web.rs b/wgpu/src/backend/web.rs index 93b4a65370..19daf02785 100644 --- a/wgpu/src/backend/web.rs +++ b/wgpu/src/backend/web.rs @@ -1026,23 +1026,51 @@ impl Context { pub fn instance_create_surface_from_canvas( &self, canvas: &web_sys::HtmlCanvasElement, - ) -> ::SurfaceId { - let context: wasm_bindgen::JsValue = match canvas.get_context("webgpu") { - Ok(Some(ctx)) => ctx.into(), - _ => panic!("expected to get context from canvas"), - }; - create_identified(context.into()) + ) -> Result<::SurfaceId, crate::CreateSurfaceError> { + self.create_surface_from_context(canvas.get_context("webgpu")) } pub fn instance_create_surface_from_offscreen_canvas( &self, canvas: &web_sys::OffscreenCanvas, - ) -> ::SurfaceId { - let context: wasm_bindgen::JsValue = match canvas.get_context("webgpu") { - Ok(Some(ctx)) => ctx.into(), - _ => panic!("expected to get context from canvas"), + ) -> Result<::SurfaceId, crate::CreateSurfaceError> { + self.create_surface_from_context(canvas.get_context("webgpu")) + } + + /// Common portion of public `instance_create_surface_from_*` functions. + /// + /// Note: Analogous code also exists in the WebGL2 backend at + /// `wgpu_hal::gles::web::Instance`. + fn create_surface_from_context( + &self, + context_result: Result, wasm_bindgen::JsValue>, + ) -> Result<::SurfaceId, crate::CreateSurfaceError> { + let context: js_sys::Object = match context_result { + Ok(Some(context)) => context, + Ok(None) => { + // + // A getContext() call “returns null if contextId is not supported, or if the + // canvas has already been initialized with another context type”. Additionally, + // “not supported” could include “insufficient GPU resources” or “the GPU process + // previously crashed”. So, we must return it as an `Err` since it could occur + // for circumstances outside the application author's control. + return Err(crate::CreateSurfaceError {}); + } + Err(js_error) => { + // + // A thrown exception indicates misuse of the canvas state. Ideally we wouldn't + // panic in this case ... TODO + panic!("canvas.getContext() threw {js_error:?}") + } }; - create_identified(context.into()) + + // Not returning this error because it is a type error that shouldn't happen unless + // the browser, JS builtin objects, or wasm bindings are misbehaving somehow. + let context: web_sys::GpuCanvasContext = context + .dyn_into() + .expect("canvas context is not a GPUCanvasContext"); + + Ok(create_identified(context)) } pub fn queue_copy_external_image_to_texture( @@ -1141,7 +1169,7 @@ impl crate::Context for Context { &self, _display_handle: raw_window_handle::RawDisplayHandle, window_handle: raw_window_handle::RawWindowHandle, - ) -> Self::SurfaceId { + ) -> Result { let canvas_attribute = match window_handle { raw_window_handle::RawWindowHandle::Web(web_handle) => web_handle.id, _ => panic!("expected valid handle for canvas"), diff --git a/wgpu/src/lib.rs b/wgpu/src/lib.rs index fb1cb6987e..066635ff1f 100644 --- a/wgpu/src/lib.rs +++ b/wgpu/src/lib.rs @@ -205,7 +205,7 @@ trait Context: Debug + Send + Sized + Sync { &self, display_handle: raw_window_handle::RawDisplayHandle, window_handle: raw_window_handle::RawWindowHandle, - ) -> Self::SurfaceId; + ) -> Result; fn instance_request_adapter( &self, options: &RequestAdapterOptions<'_>, @@ -1213,7 +1213,7 @@ pub struct BufferBinding<'a> { } static_assertions::assert_impl_all!(BufferBinding: Send, Sync); -/// Operation to perform to the output attachment at the start of a renderpass. +/// Operation to perform to the output attachment at the start of a render pass. /// /// The render target must be cleared at least once before its content is loaded. /// @@ -1369,8 +1369,8 @@ pub struct TextureViewDescriptor<'a> { pub label: Label<'a>, /// Format of the texture view. At this time, it must be the same as the underlying format of the texture. pub format: Option, - /// The dimension of the texture view. For 1D textures, this must be `1D`. For 2D textures it must be one of - /// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `3D` + /// The dimension of the texture view. For 1D textures, this must be `D1`. For 2D textures it must be one of + /// `D2`, `D2Array`, `Cube`, and `CubeArray`. For 3D textures it must be `D3` pub dimension: Option, /// Aspect of the texture. Color textures must be [`TextureAspect::All`]. pub aspect: TextureAspect, @@ -1674,13 +1674,13 @@ pub struct RenderBundleEncoderDescriptor<'a> { /// Debug label of the render bundle encoder. This will show up in graphics debuggers for easy identification. pub label: Label<'a>, /// The formats of the color attachments that this render bundle is capable to rendering to. This - /// must match the formats of the color attachments in the renderpass this render bundle is executed in. + /// must match the formats of the color attachments in the render pass this render bundle is executed in. pub color_formats: &'a [Option], /// Information about the depth attachment that this render bundle is capable to rendering to. This - /// must match the format of the depth attachments in the renderpass this render bundle is executed in. + /// must match the format of the depth attachments in the render pass this render bundle is executed in. pub depth_stencil: Option, /// Sample count this render bundle is capable of rendering to. This must match the pipelines and - /// the renderpasses it is used in. + /// the render passes it is used in. pub sample_count: u32, /// If this render bundle will rendering to multiple array layers in the attachments at the same time. pub multiview: Option, @@ -1845,23 +1845,34 @@ impl Instance { /// /// # Safety /// - /// - Raw Window Handle must be a valid object to create a surface upon and - /// must remain valid for the lifetime of the returned surface. - /// - If not called on the main thread, metal backend will panic. + /// - `raw_window_handle` must be a valid object to create a surface upon. + /// - `raw_window_handle` must remain valid until after the returned [`Surface`] is + /// dropped. + /// + /// # Errors + /// + /// - On WebGL2: Will return an error if the browser does not support WebGL2, + /// or declines to provide GPU access (such as due to a resource shortage). + /// + /// # Panics + /// + /// - On macOS/Metal: will panic if not called on the main thread. + /// - On web: will panic if the `raw_window_handle` does not properly refer to a + /// canvas element. pub unsafe fn create_surface< W: raw_window_handle::HasRawWindowHandle + raw_window_handle::HasRawDisplayHandle, >( &self, window: &W, - ) -> Surface { - Surface { + ) -> Result { + Ok(Surface { context: Arc::clone(&self.context), id: Context::instance_create_surface( &*self.context, raw_window_handle::HasRawDisplayHandle::raw_display_handle(window), raw_window_handle::HasRawWindowHandle::raw_window_handle(window), - ), - } + )?, + }) } /// Creates a surface from `CoreAnimationLayer`. @@ -1891,29 +1902,42 @@ impl Instance { /// /// The `canvas` argument must be a valid `` element to /// create a surface upon. + /// + /// # Errors + /// + /// - On WebGL2: Will return an error if the browser does not support WebGL2, + /// or declines to provide GPU access (such as due to a resource shortage). #[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))] - pub fn create_surface_from_canvas(&self, canvas: &web_sys::HtmlCanvasElement) -> Surface { - Surface { + pub fn create_surface_from_canvas( + &self, + canvas: &web_sys::HtmlCanvasElement, + ) -> Result { + Ok(Surface { context: Arc::clone(&self.context), - id: self.context.instance_create_surface_from_canvas(canvas), - } + id: self.context.instance_create_surface_from_canvas(canvas)?, + }) } /// Creates a surface from a `web_sys::OffscreenCanvas`. /// /// The `canvas` argument must be a valid `OffscreenCanvas` object /// to create a surface upon. + /// + /// # Errors + /// + /// - On WebGL2: Will return an error if the browser does not support WebGL2, + /// or declines to provide GPU access (such as due to a resource shortage). #[cfg(all(target_arch = "wasm32", not(feature = "emscripten")))] pub fn create_surface_from_offscreen_canvas( &self, canvas: &web_sys::OffscreenCanvas, - ) -> Surface { - Surface { + ) -> Result { + Ok(Surface { context: Arc::clone(&self.context), id: self .context - .instance_create_surface_from_offscreen_canvas(canvas), - } + .instance_create_surface_from_offscreen_canvas(canvas)?, + }) } /// Polls all devices. @@ -2407,6 +2431,22 @@ impl Display for RequestDeviceError { impl error::Error for RequestDeviceError {} +/// [`Instance::create_surface()`] or a related function failed. +#[derive(Clone, PartialEq, Eq, Debug)] +#[non_exhaustive] +pub struct CreateSurfaceError { + // TODO: Report diagnostic clues +} +static_assertions::assert_impl_all!(CreateSurfaceError: Send, Sync); + +impl Display for CreateSurfaceError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Creating a surface failed") + } +} + +impl error::Error for CreateSurfaceError {} + /// Error occurred when trying to async map a buffer. #[derive(Clone, PartialEq, Eq, Debug)] pub struct BufferAsyncError; @@ -3162,7 +3202,7 @@ impl<'a> RenderPass<'a> { /// [`Features::MULTI_DRAW_INDIRECT_COUNT`] must be enabled on the device in order to call these functions. impl<'a> RenderPass<'a> { - /// Disptaches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`. + /// Dispatches multiple draw calls from the active vertex buffer(s) based on the contents of the `indirect_buffer`. /// The count buffer is read to determine how many draws to issue. /// /// The indirect buffer must be long enough to account for `max_count` draws, however only `count` will @@ -4055,14 +4095,14 @@ impl UncapturedErrorHandler for T where T: Fn(Error) + Send + 'static {} pub enum Error { /// Out of memory error OutOfMemory { - /// + /// Lower level source of the error. source: Box, }, /// Validation error, signifying a bug in code or data Validation { - /// + /// Lower level source of the error. source: Box, - /// + /// Description of the validation error. description: String, }, } diff --git a/wgpu/tests/buffer_usages.rs b/wgpu/tests/buffer_usages.rs index db734c108b..7eb3f0e832 100644 --- a/wgpu/tests/buffer_usages.rs +++ b/wgpu/tests/buffer_usages.rs @@ -1,11 +1,13 @@ //! Tests for buffer usages validation. use crate::common::{fail_if, initialize_test, TestParameters}; +use wasm_bindgen_test::*; use wgt::BufferAddress; const BUFFER_SIZE: BufferAddress = 1234; #[test] +#[wasm_bindgen_test] fn buffer_usage() { fn try_create(enable_mappable_primary_buffers: bool, usages: &[(bool, &[wgpu::BufferUsages])]) { let mut parameters = TestParameters::default(); diff --git a/wgpu/tests/clear_texture.rs b/wgpu/tests/clear_texture.rs index abe86cac43..0e8a75f3b0 100644 --- a/wgpu/tests/clear_texture.rs +++ b/wgpu/tests/clear_texture.rs @@ -1,4 +1,5 @@ use crate::common::{initialize_test, TestParameters, TestingContext}; +use wasm_bindgen_test::*; use wgpu::util::align_to; static TEXTURE_FORMATS_UNCOMPRESSED: &[wgpu::TextureFormat] = &[ @@ -202,9 +203,11 @@ fn single_texture_clear_test( size: wgpu::Extent3d, dimension: wgpu::TextureDimension, ) { - println!( + log::info!( "clearing texture with {:?}, dimension {:?}, size {:?}", - format, dimension, size + format, + dimension, + size ); let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { @@ -315,6 +318,7 @@ fn clear_texture_2d_uncompressed() { } #[test] +#[wasm_bindgen_test] fn clear_texture_d32_s8() { initialize_test( TestParameters::default() diff --git a/wgpu/tests/common/isolation.rs b/wgpu/tests/common/isolation.rs new file mode 100644 index 0000000000..e4c7b6e560 --- /dev/null +++ b/wgpu/tests/common/isolation.rs @@ -0,0 +1,43 @@ +use std::sync::atomic::{AtomicBool, Ordering}; + +/// True if a test is in progress somewhere in the process, false otherwise. +static TEST_ACTIVE_IN_PROCESS: AtomicBool = AtomicBool::new(false); + +const OTHER_TEST_IN_PROGRESS_ERROR: &str = "TEST ISOLATION ERROR: + +wgpu's test harness requires that no more than one test is running per process. + +The best way to facilitate this is by using cargo-nextest which runs each test in its own process +and has a very good testing UI: + +cargo install cargo-nextest +cargo nextest run + +Alternatively, you can run tests in single threaded mode (much slower). + +cargo test -- --test-threads=1 + +Calling std::process::abort()... +"; + +/// When this guard is active, enforces that there is only a single test running in the process +/// at any one time. If there are multiple processes, creating the guard hard terminates the process. +pub struct OneTestPerProcessGuard(()); + +impl OneTestPerProcessGuard { + pub fn new() -> Self { + let other_tests_in_flight = TEST_ACTIVE_IN_PROCESS.swap(true, Ordering::SeqCst); + if other_tests_in_flight { + log::error!("{}", OTHER_TEST_IN_PROGRESS_ERROR); + // Hard exit to call attention to the error + std::process::abort(); + } + OneTestPerProcessGuard(()) + } +} + +impl Drop for OneTestPerProcessGuard { + fn drop(&mut self) { + TEST_ACTIVE_IN_PROCESS.store(false, Ordering::SeqCst); + } +} diff --git a/wgpu/tests/common/mod.rs b/wgpu/tests/common/mod.rs index dedf80f5a3..37fafed043 100644 --- a/wgpu/tests/common/mod.rs +++ b/wgpu/tests/common/mod.rs @@ -3,11 +3,13 @@ use std::panic::{catch_unwind, AssertUnwindSafe}; +use wgpu::{Adapter, Device, DownlevelFlags, Instance, Queue, Surface}; use wgt::{Backends, DeviceDescriptor, DownlevelCapabilities, Features, Limits}; -use wgpu::{util, Adapter, Device, DownlevelFlags, Instance, Queue}; - pub mod image; +mod isolation; + +const CANVAS_ID: &str = "test-canvas"; async fn initialize_device( adapter: &Adapter, @@ -166,18 +168,17 @@ impl TestParameters { self } } + pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(TestingContext)) { // We don't actually care if it fails + #[cfg(not(target_arch = "wasm32"))] let _ = env_logger::try_init(); + #[cfg(target_arch = "wasm32")] + let _ = console_log::init_with_level(log::Level::Info); - let backend_bits = util::backend_bits_from_env().unwrap_or_else(Backends::all); - let instance = Instance::new(backend_bits); - let adapter = pollster::block_on(util::initialize_adapter_from_env_or_default( - &instance, - backend_bits, - None, - )) - .expect("could not find suitable adapter on the system"); + let _test_guard = isolation::OneTestPerProcessGuard::new(); + + let (adapter, _) = initialize_adapter(); let adapter_info = adapter.get_info(); let adapter_lowercase_name = adapter_info.name.to_lowercase(); @@ -187,19 +188,19 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te let missing_features = parameters.required_features - adapter_features; if !missing_features.is_empty() { - println!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features); + log::info!("TEST SKIPPED: MISSING FEATURES {:?}", missing_features); return; } if !parameters.required_limits.check_limits(&adapter_limits) { - println!("TEST SKIPPED: LIMIT TOO LOW"); + log::info!("TEST SKIPPED: LIMIT TOO LOW"); return; } let missing_downlevel_flags = parameters.required_downlevel_properties.flags - adapter_downlevel_capabilities.flags; if !missing_downlevel_flags.is_empty() { - println!( + log::info!( "TEST SKIPPED: MISSING DOWNLEVEL FLAGS {:?}", missing_downlevel_flags ); @@ -209,7 +210,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te if adapter_downlevel_capabilities.shader_model < parameters.required_downlevel_properties.shader_model { - println!( + log::info!( "TEST SKIPPED: LOW SHADER MODEL {:?}", adapter_downlevel_capabilities.shader_model ); @@ -273,7 +274,7 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te }); if let Some((reason, true)) = expected_failure_reason { - println!("EXPECTED TEST FAILURE SKIPPED: {:?}", reason); + log::info!("EXPECTED TEST FAILURE SKIPPED: {:?}", reason); return; } @@ -301,9 +302,10 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te // We got the conditions we expected if let Some((expected_reason, _)) = expected_failure_reason { // Print out reason for the failure - println!( + log::info!( "GOT EXPECTED TEST FAILURE DUE TO {}: {:?}", - failure_cause, expected_reason + failure_cause, + expected_reason ); } } else if let Some((reason, _)) = expected_failure_reason { @@ -314,6 +316,72 @@ pub fn initialize_test(parameters: TestParameters, test_function: impl FnOnce(Te } } +fn initialize_adapter() -> (Adapter, SurfaceGuard) { + let backend_bits = wgpu::util::backend_bits_from_env().unwrap_or_else(Backends::all); + let instance = Instance::new(backend_bits); + let compatible_surface; + + #[cfg(not(all(target_arch = "wasm32", feature = "webgl")))] + { + compatible_surface = None; + } + #[cfg(all(target_arch = "wasm32", feature = "webgl"))] + { + // On wasm, append a canvas to the document body for initializing the adapter + let canvas = create_html_canvas(); + + let surface = instance + .create_surface_from_canvas(&canvas) + .expect("could not create surface from canvas"); + + compatible_surface = Some(surface); + } + + let compatible_surface: Option<&Surface> = compatible_surface.as_ref(); + let adapter = pollster::block_on(wgpu::util::initialize_adapter_from_env_or_default( + &instance, + backend_bits, + compatible_surface, + )) + .expect("could not find suitable adapter on the system"); + + (adapter, SurfaceGuard) +} + +struct SurfaceGuard; + +#[cfg(all(target_arch = "wasm32", feature = "webgl"))] +impl Drop for SurfaceGuard { + fn drop(&mut self) { + delete_html_canvas(); + } +} + +#[cfg(all(target_arch = "wasm32", feature = "webgl"))] +fn create_html_canvas() -> web_sys::HtmlCanvasElement { + use wasm_bindgen::JsCast; + + web_sys::window() + .and_then(|win| win.document()) + .and_then(|doc| { + let body = doc.body().unwrap(); + let canvas = doc.create_element("Canvas").unwrap(); + canvas.set_id(CANVAS_ID); + body.append_child(&canvas).unwrap(); + canvas.dyn_into::().ok() + }) + .expect("couldn't append canvas to document body") +} + +#[cfg(all(target_arch = "wasm32", feature = "webgl"))] +fn delete_html_canvas() { + if let Some(document) = web_sys::window().and_then(|win| win.document()) { + if let Some(element) = document.get_element_by_id(CANVAS_ID) { + element.remove(); + } + }; +} + // Run some code in an error scope and assert that validation fails. pub fn fail(device: &wgpu::Device, callback: impl FnOnce() -> T) -> T { device.push_error_scope(wgpu::ErrorFilter::Validation); diff --git a/wgpu/tests/device.rs b/wgpu/tests/device.rs index 2763a6a21a..69da6b9f3e 100644 --- a/wgpu/tests/device.rs +++ b/wgpu/tests/device.rs @@ -1,6 +1,9 @@ +use wasm_bindgen_test::*; + use crate::common::{initialize_test, TestParameters}; #[test] +#[wasm_bindgen_test] fn device_initialization() { initialize_test(TestParameters::default(), |_ctx| { // intentionally empty diff --git a/wgpu/tests/encoder.rs b/wgpu/tests/encoder.rs index f722f8e34b..119f2cad8d 100644 --- a/wgpu/tests/encoder.rs +++ b/wgpu/tests/encoder.rs @@ -1,6 +1,8 @@ use crate::common::{initialize_test, TestParameters}; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn drop_encoder() { initialize_test(TestParameters::default(), |ctx| { let encoder = ctx diff --git a/wgpu/tests/instance.rs b/wgpu/tests/instance.rs index c5de0480bd..1ead17c57e 100644 --- a/wgpu/tests/instance.rs +++ b/wgpu/tests/instance.rs @@ -1,4 +1,7 @@ +use wasm_bindgen_test::*; + #[test] +#[wasm_bindgen_test] fn initialize() { let _ = wgpu::Instance::new( wgpu::util::backend_bits_from_env().unwrap_or_else(wgpu::Backends::all), diff --git a/wgpu/tests/poll.rs b/wgpu/tests/poll.rs index 6113436d0b..77cac2a35f 100644 --- a/wgpu/tests/poll.rs +++ b/wgpu/tests/poll.rs @@ -7,6 +7,7 @@ use wgpu::{ }; use crate::common::{initialize_test, TestParameters, TestingContext}; +use wasm_bindgen_test::*; fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer { let buffer = ctx.device.create_buffer(&BufferDescriptor { @@ -53,6 +54,7 @@ fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer { } #[test] +#[wasm_bindgen_test] fn wait() { initialize_test(TestParameters::default().skip(), |ctx| { let cmd_buf = generate_dummy_work(&ctx); @@ -63,6 +65,7 @@ fn wait() { } #[test] +#[wasm_bindgen_test] fn double_wait() { initialize_test(TestParameters::default().skip(), |ctx| { let cmd_buf = generate_dummy_work(&ctx); @@ -74,6 +77,7 @@ fn double_wait() { } #[test] +#[wasm_bindgen_test] fn wait_on_submission() { initialize_test(TestParameters::default().skip(), |ctx| { let cmd_buf = generate_dummy_work(&ctx); @@ -84,6 +88,7 @@ fn wait_on_submission() { } #[test] +#[wasm_bindgen_test] fn double_wait_on_submission() { initialize_test(TestParameters::default().skip(), |ctx| { let cmd_buf = generate_dummy_work(&ctx); @@ -95,6 +100,7 @@ fn double_wait_on_submission() { } #[test] +#[wasm_bindgen_test] fn wait_out_of_order() { initialize_test(TestParameters::default().skip(), |ctx| { let cmd_buf1 = generate_dummy_work(&ctx); diff --git a/wgpu/tests/queue_transfer.rs b/wgpu/tests/queue_transfer.rs index 7724c291cd..90310ea6e7 100644 --- a/wgpu/tests/queue_transfer.rs +++ b/wgpu/tests/queue_transfer.rs @@ -3,8 +3,10 @@ use std::num::NonZeroU32; use crate::common::{fail, initialize_test, TestParameters}; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn queue_write_texture_overflow() { initialize_test(TestParameters::default(), |ctx| { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { diff --git a/wgpu/tests/resource_descriptor_accessor.rs b/wgpu/tests/resource_descriptor_accessor.rs index f43a996a40..cbb3aac422 100644 --- a/wgpu/tests/resource_descriptor_accessor.rs +++ b/wgpu/tests/resource_descriptor_accessor.rs @@ -1,7 +1,9 @@ use crate::common::{initialize_test, TestParameters}; +use wasm_bindgen_test::*; /// Buffer's size and usage can be read back. #[test] +#[wasm_bindgen_test] fn buffer_size_and_usage() { initialize_test(TestParameters::default(), |ctx| { let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { diff --git a/wgpu/tests/resource_error.rs b/wgpu/tests/resource_error.rs index 81d50e5800..716c646a7d 100644 --- a/wgpu/tests/resource_error.rs +++ b/wgpu/tests/resource_error.rs @@ -1,6 +1,8 @@ use crate::common::{fail, initialize_test, valid, TestParameters}; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn bad_buffer() { // Create a buffer with bad parameters and call a few methods. // Validation should fail but there should be not panic. @@ -24,6 +26,7 @@ fn bad_buffer() { } #[test] +#[wasm_bindgen_test] fn bad_texture() { // Create a texture with bad parameters and call a few methods. // Validation should fail but there should be not panic. diff --git a/wgpu/tests/root.rs b/wgpu/tests/root.rs index e721e3a7a7..a3132eda81 100644 --- a/wgpu/tests/root.rs +++ b/wgpu/tests/root.rs @@ -1,3 +1,5 @@ +use wasm_bindgen_test::wasm_bindgen_test_configure; + // All files containing tests mod common; @@ -20,3 +22,5 @@ mod transfer; mod vertex_indices; mod write_texture; mod zero_init_texture_after_discard; + +wasm_bindgen_test_configure!(run_in_browser); diff --git a/wgpu/tests/shader/numeric_builtins.rs b/wgpu/tests/shader/numeric_builtins.rs index 83b278cfbf..c215054cc3 100644 --- a/wgpu/tests/shader/numeric_builtins.rs +++ b/wgpu/tests/shader/numeric_builtins.rs @@ -1,3 +1,4 @@ +use wasm_bindgen_test::*; use wgpu::{DownlevelFlags, Limits}; use crate::{ @@ -40,6 +41,7 @@ fn create_numeric_builtin_test() -> Vec { } #[test] +#[wasm_bindgen_test] fn numeric_builtins() { initialize_test( TestParameters::default() diff --git a/wgpu/tests/shader/struct_layout.rs b/wgpu/tests/shader/struct_layout.rs index 2250143b5f..c0bba4d1ed 100644 --- a/wgpu/tests/shader/struct_layout.rs +++ b/wgpu/tests/shader/struct_layout.rs @@ -1,5 +1,6 @@ use std::fmt::Write; +use wasm_bindgen_test::*; use wgpu::{Backends, DownlevelFlags, Features, Limits}; use crate::{ @@ -177,6 +178,7 @@ fn create_struct_layout_tests(storage_type: InputStorageType) -> Vec } #[test] +#[wasm_bindgen_test] fn uniform_input() { initialize_test( TestParameters::default() @@ -193,6 +195,7 @@ fn uniform_input() { } #[test] +#[wasm_bindgen_test] fn storage_input() { initialize_test( TestParameters::default() @@ -209,6 +212,7 @@ fn storage_input() { } #[test] +#[wasm_bindgen_test] fn push_constant_input() { initialize_test( TestParameters::default() diff --git a/wgpu/tests/shader_primitive_index/mod.rs b/wgpu/tests/shader_primitive_index/mod.rs index 5e6c6b1b70..a7f68936f2 100644 --- a/wgpu/tests/shader_primitive_index/mod.rs +++ b/wgpu/tests/shader_primitive_index/mod.rs @@ -1,5 +1,6 @@ use crate::common::{initialize_test, TestParameters, TestingContext}; use std::num::NonZeroU32; +use wasm_bindgen_test::*; use wgpu::util::{align_to, DeviceExt}; // @@ -37,6 +38,7 @@ use wgpu::util::{align_to, DeviceExt}; // buffer [3, 4, 5, 0, 1, 2]. This also swaps the resulting pixel colors. // #[test] +#[wasm_bindgen_test] fn draw() { // // +-----+-----+ @@ -61,6 +63,7 @@ fn draw() { } #[test] +#[wasm_bindgen_test] fn draw_indexed() { // // +-----+-----+ diff --git a/wgpu/tests/texture_bounds.rs b/wgpu/tests/texture_bounds.rs index 2796815198..635dac8480 100644 --- a/wgpu/tests/texture_bounds.rs +++ b/wgpu/tests/texture_bounds.rs @@ -1,30 +1,32 @@ //! Tests for texture copy bounds checks. -use crate::common::{initialize_test, TestParameters}; +use crate::common::{fail_if, initialize_test, TestParameters}; use std::num::NonZeroU32; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn bad_copy_origin() { fn try_origin(origin: wgpu::Origin3d, size: wgpu::Extent3d, should_panic: bool) { - let mut parameters = TestParameters::default(); - if should_panic { - parameters = parameters.failure(); - } + let parameters = TestParameters::default(); initialize_test(parameters, |ctx| { let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR); let data = vec![255; BUFFER_SIZE as usize]; - ctx.queue.write_texture( - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin, - aspect: wgpu::TextureAspect::All, - }, - &data, - BUFFER_COPY_LAYOUT, - size, - ); + + fail_if(&ctx.device, should_panic, || { + ctx.queue.write_texture( + wgpu::ImageCopyTexture { + texture: &texture, + mip_level: 0, + origin, + aspect: wgpu::TextureAspect::All, + }, + &data, + BUFFER_COPY_LAYOUT, + size, + ) + }); }); } diff --git a/wgpu/tests/vertex_indices/mod.rs b/wgpu/tests/vertex_indices/mod.rs index 177b857448..2c739e8b3d 100644 --- a/wgpu/tests/vertex_indices/mod.rs +++ b/wgpu/tests/vertex_indices/mod.rs @@ -1,5 +1,6 @@ use std::num::NonZeroU64; +use wasm_bindgen_test::*; use wgpu::util::DeviceExt; use crate::common::{initialize_test, TestParameters, TestingContext}; @@ -131,6 +132,7 @@ fn pulling_common( } #[test] +#[wasm_bindgen_test] fn draw() { initialize_test(TestParameters::default().test_features_limits(), |ctx| { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { @@ -140,6 +142,7 @@ fn draw() { } #[test] +#[wasm_bindgen_test] fn draw_vertex_offset() { initialize_test( TestParameters::default() @@ -155,6 +158,7 @@ fn draw_vertex_offset() { } #[test] +#[wasm_bindgen_test] fn draw_instanced() { initialize_test(TestParameters::default().test_features_limits(), |ctx| { pulling_common(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { @@ -164,6 +168,7 @@ fn draw_instanced() { } #[test] +#[wasm_bindgen_test] fn draw_instanced_offset() { initialize_test( TestParameters::default() diff --git a/wgpu/tests/write_texture.rs b/wgpu/tests/write_texture.rs index 742e97d818..9d351ed70c 100644 --- a/wgpu/tests/write_texture.rs +++ b/wgpu/tests/write_texture.rs @@ -3,8 +3,10 @@ use crate::common::{initialize_test, TestParameters}; use std::num::NonZeroU32; +use wasm_bindgen_test::*; #[test] +#[wasm_bindgen_test] fn write_texture_subset() { let size = 256; let parameters = TestParameters::default().backend_failure(wgpu::Backends::DX12); diff --git a/wgpu/tests/zero_init_texture_after_discard.rs b/wgpu/tests/zero_init_texture_after_discard.rs index 6043d0388f..3c584eb7e4 100644 --- a/wgpu/tests/zero_init_texture_after_discard.rs +++ b/wgpu/tests/zero_init_texture_after_discard.rs @@ -1,9 +1,11 @@ use std::num::NonZeroU32; use crate::common::{initialize_test, TestParameters}; +use wasm_bindgen_test::*; // Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder. #[test] +#[wasm_bindgen_test] fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after_submit() { initialize_test(TestParameters::default(), |ctx| { let (texture, readback_buffer) = @@ -39,6 +41,7 @@ fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_after // Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in the same encoder to a buffer. #[test] +#[wasm_bindgen_test] fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() { initialize_test(TestParameters::default(), |ctx| { let (texture, readback_buffer) = @@ -67,6 +70,7 @@ fn discarding_color_target_resets_texture_init_state_check_visible_on_copy_in_sa } #[test] +#[wasm_bindgen_test] #[allow(clippy::single_element_loop)] fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_same_encoder() { initialize_test( @@ -109,6 +113,7 @@ fn discarding_depth_target_resets_texture_init_state_check_visible_on_copy_in_sa } #[test] +#[wasm_bindgen_test] fn discarding_either_depth_or_stencil_aspect() { initialize_test(TestParameters::default(), |ctx| { let (texture, _) = create_white_texture_and_readback_buffer(