diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 2d63086987..bbcfe868fa 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -4,5 +4,5 @@ contact_links: url: https://github.com/gfx-rs/naga/issues/new/choose about: Issues with or enhancements for the shader translation. - name: Question about wgpu - url: https://github.com/gfx-rs/wgpu-rs/discussions/new + url: https://github.com/gfx-rs/wgpu/discussions/new about: Any questions about how to use wgpu should go here. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4961a9fed6..50ffd7b919 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: android_build: name: Android Stable - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest env: TARGET: aarch64-linux-android PKG_CONFIG_ALLOW_CROSS: 1 @@ -35,8 +35,9 @@ jobs: run: cargo check --manifest-path wgpu-core/Cargo.toml --features trace --target ${{ env.TARGET }} wasm: + if: false # disable until hal/Gles backend is setup name: Web Assembly - runs-on: ubuntu-18.04 + runs-on: ubuntu-latest env: RUSTFLAGS: --cfg=web_sys_unstable_apis steps: @@ -53,7 +54,7 @@ jobs: strategy: fail-fast: false matrix: - os: [macos-10.15, ubuntu-18.04, windows-2019] + os: [macos-10.15, ubuntu-20.04, windows-2019] channel: [stable, nightly] include: - name: MacOS Stable @@ -69,18 +70,18 @@ jobs: additional_core_features: additional_player_features: - name: Ubuntu Stable - os: ubuntu-18.04 + os: ubuntu-20.04 channel: stable prepare_command: additional_core_features: trace,replay additional_player_features: - name: Ubuntu Nightly - os: ubuntu-18.04 + os: ubuntu-20.04 channel: nightly prepare_command: | echo "Installing Vulkan" sudo apt-get update -y -qq - sudo add-apt-repository ppa:kisak/kisak-mesa -y + sudo add-apt-repository ppa:ubuntu-x-swat/updates -y sudo apt-get update sudo apt install -y libxcb-xfixes0-dev mesa-vulkan-drivers additional_core_features: serial-pass @@ -123,7 +124,7 @@ jobs: run: cargo test -- --nocapture docs: - runs-on: [ubuntu-18.04] + runs-on: [ubuntu-latest] steps: - uses: actions/checkout@v2 - name: Install latest nightly diff --git a/CHANGELOG.md b/CHANGELOG.md index 7333e7b3ab..49f581a8af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ # Change Log TBD: - - Merged wgpu-rs and wgpu back into a single repository + - Crates: + - Merged wgpu-rs and wgpu back into a single repository + - Replaced gfx-rs dependencies by the new `wgpu-hal` ## v0.8 (2021-04-29) - Naga is used by default to translate shaders, SPIRV-Cross is optional behind `cross` feature diff --git a/Cargo.lock b/Cargo.lock index 5995767d04..bf0fff1079 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -22,9 +22,9 @@ checksum = "aae1277d39aeec15cb388266ecc24b11c80469deae6067e17a1a7aa9e5c1f234" [[package]] name = "ahash" -version = "0.4.6" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6789e291be47ace86a60303502173d84af8327e3627ecf334356ee0f87a164c" +checksum = "739f4a8db6605981345c5654f3a85b056ce52f37a39d34da03f25bf2151ea16e" [[package]] name = "aho-corasick" @@ -68,9 +68,9 @@ dependencies = [ [[package]] name = "ash" -version = "0.32.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ea56be8250318e64923c7e65b82a35b5c29dfb6dc1c7d1c0b288c4b1bbb084" +checksum = "06063a002a77d2734631db74e8f4ce7148b77fe522e6bca46f2ae7774fd48112" dependencies = [ "libloading 0.7.0", ] @@ -198,9 +198,6 @@ name = "cc" version = "1.0.62" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1770ced377336a88a67c473594ccc14eca6f4559217c34f64aac8f83d641b40" -dependencies = [ - "jobserver", -] [[package]] name = "cfg-if" @@ -456,17 +453,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "d3d12" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091ed1b25fe47c7ff129fc440c23650b6114f36aa00bc7212cc8041879294428" -dependencies = [ - "bitflags", - "libloading 0.7.0", - "winapi 0.3.9", -] - [[package]] name = "darling" version = "0.10.2" @@ -704,150 +690,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "gfx-auxil" -version = "0.9.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "fxhash", - "gfx-hal", - "spirv_cross", -] - -[[package]] -name = "gfx-backend-dx11" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bitflags", - "gfx-auxil", - "gfx-hal", - "libloading 0.7.0", - "log", - "parking_lot", - "range-alloc", - "raw-window-handle", - "smallvec", - "spirv_cross", - "thunderdome", - "winapi 0.3.9", - "wio", -] - -[[package]] -name = "gfx-backend-dx12" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bit-set", - "bitflags", - "d3d12", - "gfx-auxil", - "gfx-hal", - "log", - "parking_lot", - "range-alloc", - "raw-window-handle", - "smallvec", - "spirv_cross", - "thunderdome", - "winapi 0.3.9", -] - -[[package]] -name = "gfx-backend-empty" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "gfx-hal", - "log", - "raw-window-handle", -] - -[[package]] -name = "gfx-backend-gl" -version = "0.8.1" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bitflags", - "fxhash", - "gfx-auxil", - "gfx-hal", - "glow", - "js-sys", - "khronos-egl", - "libloading 0.7.0", - "log", - "naga", - "parking_lot", - "raw-window-handle", - "spirv_cross", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "gfx-backend-metal" -version = "0.8.1" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "bitflags", - "block", - "cocoa-foundation", - "copyless", - "foreign-types", - "fxhash", - "gfx-auxil", - "gfx-hal", - "log", - "metal", - "naga", - "objc", - "parking_lot", - "profiling", - "range-alloc", - "raw-window-handle", - "spirv_cross", - "storage-map", -] - -[[package]] -name = "gfx-backend-vulkan" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "arrayvec", - "ash", - "byteorder", - "core-graphics-types", - "gfx-hal", - "inplace_it", - "libloading 0.7.0", - "log", - "naga", - "objc", - "parking_lot", - "raw-window-handle", - "renderdoc-sys", - "smallvec", - "winapi 0.3.9", -] - -[[package]] -name = "gfx-hal" -version = "0.8.0" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" -dependencies = [ - "bitflags", - "naga", - "raw-window-handle", - "thiserror", -] - [[package]] name = "gif" version = "0.11.2" @@ -858,23 +700,11 @@ dependencies = [ "weezl", ] -[[package]] -name = "glow" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b80b98efaa8a34fce11d60dd2ce2760d5d83c373cbcc73bb87c2a3a84a54108" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "gpu-alloc" -version = "0.4.4" +version = "0.4.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8159cab119e2c6947476a8b941d478c8de4a1ce050d92c55903f8d0192ccacda" +checksum = "cbc1b6ca374e81862526786d9cb42357ce03706ed1b8761730caafd02ab91f3a" dependencies = [ "bitflags", "gpu-alloc-types", @@ -1001,15 +831,6 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" -[[package]] -name = "jobserver" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" -dependencies = [ - "libc", -] - [[package]] name = "jpeg-decoder" version = "0.1.22" @@ -1038,16 +859,6 @@ dependencies = [ "winapi-build", ] -[[package]] -name = "khronos-egl" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2352bd1d0bceb871cb9d40f24360c8133c11d7486b68b5381c1dd1a32015e3" -dependencies = [ - "libc", - "libloading 0.7.0", -] - [[package]] name = "lazy_static" version = "1.4.0" @@ -1157,12 +968,11 @@ dependencies = [ [[package]] name = "metal" version = "0.22.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c12e48c737ee9a55e8bb2352bcde588f79ae308d3529ee888f7cc0f469b5777" +source = "git+https://github.com/gfx-rs/metal-rs?rev=08cc15a3be5a57fc07bb27091eff3569dd60cfd3#08cc15a3be5a57fc07bb27091eff3569dd60cfd3" dependencies = [ "bitflags", "block", - "cocoa-foundation", + "core-graphics-types", "foreign-types", "log", "objc", @@ -1639,11 +1449,6 @@ dependencies = [ "rand_core", ] -[[package]] -name = "range-alloc" -version = "0.1.2" -source = "git+https://github.com/gfx-rs/gfx?rev=27a1dae3796d33d23812f2bb8c7e3b5aea18b521#27a1dae3796d33d23812f2bb8c7e3b5aea18b521" - [[package]] name = "raw-window-handle" version = "0.3.3" @@ -1815,12 +1620,6 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c111b5bd5695e56cffe5129854aa230b39c93a305372fdbb2668ca2394eea9f8" -[[package]] -name = "slotmap" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c46a3482db8f247956e464d783693ece164ca056e6e67563ee5505bdb86452cd" - [[package]] name = "smallvec" version = "1.4.2" @@ -1846,17 +1645,6 @@ dependencies = [ "wayland-protocols", ] -[[package]] -name = "spirv_cross" -version = "0.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06db6bd7b6518f761593783e2896eefe55e90455efc5f44511078ce0426ed418" -dependencies = [ - "cc", - "js-sys", - "wasm-bindgen", -] - [[package]] name = "spirv_headers" version = "1.5.0" @@ -1867,15 +1655,6 @@ dependencies = [ "num-traits 0.2.14", ] -[[package]] -name = "storage-map" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418bb14643aa55a7841d5303f72cf512cfb323b8cc221d51580500a1ca75206c" -dependencies = [ - "lock_api", -] - [[package]] name = "strsim" version = "0.9.3" @@ -1931,12 +1710,6 @@ dependencies = [ "lazy_static", ] -[[package]] -name = "thunderdome" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87b4947742c93ece24a0032141d9caa3d853752e694a57e35029dd2bd08673e0" - [[package]] name = "tiff" version = "0.6.1" @@ -2187,6 +1960,7 @@ dependencies = [ "wasm-bindgen-futures", "web-sys", "wgpu-core", + "wgpu-hal", "wgpu-types", "winit", ] @@ -2200,15 +1974,6 @@ dependencies = [ "cfg_aliases", "copyless", "fxhash", - "gfx-backend-dx11", - "gfx-backend-dx12", - "gfx-backend-empty", - "gfx-backend-gl", - "gfx-backend-metal", - "gfx-backend-vulkan", - "gfx-hal", - "gpu-alloc", - "gpu-descriptor", "log", "loom", "naga", @@ -2219,7 +1984,37 @@ dependencies = [ "serde", "smallvec", "thiserror", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-hal" +version = "0.1.0" +dependencies = [ + "arrayvec", + "ash", + "bitflags", + "block", + "core-graphics-types", + "env_logger", + "foreign-types", + "fxhash", + "gpu-alloc", + "gpu-descriptor", + "inplace_it", + "libloading 0.7.0", + "log", + "metal", + "naga", + "objc", + "parking_lot", + "raw-window-handle", + "renderdoc-sys", + "thiserror", "wgpu-types", + "winapi 0.3.9", + "winit", ] [[package]] @@ -2306,15 +2101,6 @@ dependencies = [ "x11-dl", ] -[[package]] -name = "wio" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5" -dependencies = [ - "winapi 0.3.9", -] - [[package]] name = "ws2_32-sys" version = "0.2.1" diff --git a/Cargo.toml b/Cargo.toml index 5ce16a92f3..3298b4b288 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,14 @@ [workspace] +resolver = "2" members = [ "dummy", "player", "wgpu", "wgpu-core", + "wgpu-hal", "wgpu-types", ] -default-members = ["wgpu"] +default-members = ["wgpu", "player", "wgpu-hal"] [patch."https://github.com/gfx-rs/naga"] #naga = { path = "../naga" } @@ -17,15 +19,6 @@ default-members = ["wgpu"] [patch."https://github.com/zakarumych/gpu-alloc"] #gpu-alloc = { path = "../gpu-alloc/gpu-alloc" } -[patch."https://github.com/gfx-rs/gfx"] -#gfx-hal = { path = "../gfx/src/hal" } -#gfx-backend-empty = { path = "../gfx/src/backend/empty" } -#gfx-backend-vulkan = { path = "../gfx/src/backend/vulkan" } -#gfx-backend-gl = { path = "../gfx/src/backend/gl" } -#gfx-backend-dx12 = { path = "../gfx/src/backend/dx12" } -#gfx-backend-dx11 = { path = "../gfx/src/backend/dx11" } -#gfx-backend-metal = { path = "../gfx/src/backend/metal" } - [patch.crates-io] #web-sys = { path = "../wasm-bindgen/crates/web-sys" } #js-sys = { path = "../wasm-bindgen/crates/js-sys" } diff --git a/README.md b/README.md index 6f5b2c6b9d..a18829e564 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,13 @@ [![codecov.io](https://codecov.io/gh/gfx-rs/wgpu/branch/master/graph/badge.svg?token=84qJTesmeS)](https://codecov.io/gh/gfx-rs/wgpu) This is an implementation of [WebGPU](https://www.w3.org/community/gpu/) API in Rust, targeting both native and the Web. -It's written in Rust and is based on [gfx-hal](https://github.com/gfx-rs/gfx) with help of [gpu-alloc](https://github.com/zakarumych/gpu-alloc) and [gpu-descriptor](https://github.com/zakarumych/gpu-descriptor). See the upstream [WebGPU specification](https://gpuweb.github.io/gpuweb/) (work in progress). +See the upstream [WebGPU specification](https://gpuweb.github.io/gpuweb/) (work in progress). The repository hosts the following parts: - [![Crates.io](https://img.shields.io/crates/v/wgpu.svg?label=wgpu)](https://crates.io/crates/wgpu) [![docs.rs](https://docs.rs/wgpu/badge.svg)](https://docs.rs/wgpu/) - public Rust API for users - [![Crates.io](https://img.shields.io/crates/v/wgpu-core.svg?label=wgpu-core)](https://crates.io/crates/wgpu-core) [![docs.rs](https://docs.rs/wgpu-core/badge.svg)](https://docs.rs/wgpu-core/) - internal Rust API for WebGPU implementations to use + - [![Crates.io](https://img.shields.io/crates/v/wgpu-hal.svg?label=wgpu-hal)](https://crates.io/crates/wgpu-hal) [![docs.rs](https://docs.rs/wgpu-hal/badge.svg)](https://docs.rs/wgpu-hal/) - internal unsafe GPU abstraction API - [![Crates.io](https://img.shields.io/crates/v/wgpu-types.svg?label=wgpu-types)](https://crates.io/crates/wgpu-types) [![docs.rs](https://docs.rs/wgpu-types/badge.svg)](https://docs.rs/wgpu-types/) - Rust types shared between `wgpu-core` and `wgpu-rs` - `player` - standalone application for replaying the API traces, uses `winit` @@ -24,10 +25,10 @@ If you are looking for the native implementation or bindings to the API in other API | Windows 7/10 | Linux & Android | macOS & iOS | ----- | ------------------ | ------------------ | ------------------ | - DX11 | :ok: | | | - DX12 | :white_check_mark: | | | + DX11 | | | | + DX12 | | | | Vulkan | :white_check_mark: | :white_check_mark: | | Metal | | | :white_check_mark: | - GL ES3 | | :construction: | | + GLes3 | | | | :white_check_mark: = Primary support — :ok: = Secondary support — :construction: = Unsupported, but support in progress diff --git a/bors.toml b/bors.toml index 729709bdf9..ee06a2174a 100644 --- a/bors.toml +++ b/bors.toml @@ -7,6 +7,6 @@ status = [ "Ubuntu Nightly", "Windows Stable", "Windows Nightly", - "Web Assembly", + #"Web Assembly", #"Clippy", ] diff --git a/player/Cargo.toml b/player/Cargo.toml index 70b39a492e..40c8c673b0 100644 --- a/player/Cargo.toml +++ b/player/Cargo.toml @@ -13,7 +13,6 @@ license = "MPL-2.0" publish = false [features] -cross = ["wgc/cross"] [dependencies] env_logger = "0.8" diff --git a/player/src/lib.rs b/player/src/lib.rs index 91adf5e91a..881307b7c5 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -39,12 +39,12 @@ impl wgc::hub::IdentityHandlerFactory impl wgc::hub::GlobalIdentityHandlerFactory for IdentityPassThroughFactory {} pub trait GlobalPlay { - fn encode_commands( + fn encode_commands( &self, encoder: wgc::id::CommandEncoderId, commands: Vec, ) -> wgc::id::CommandBufferId; - fn process( + fn process( &self, device: wgc::id::DeviceId, action: trace::Action, @@ -54,7 +54,7 @@ pub trait GlobalPlay { } impl GlobalPlay for wgc::hub::Global { - fn encode_commands( + fn encode_commands( &self, encoder: wgc::id::CommandEncoderId, commands: Vec, @@ -68,33 +68,33 @@ impl GlobalPlay for wgc::hub::Global { dst_offset, size, } => self - .command_encoder_copy_buffer_to_buffer::( + .command_encoder_copy_buffer_to_buffer::( encoder, src, src_offset, dst, dst_offset, size, ) .unwrap(), trace::Command::CopyBufferToTexture { src, dst, size } => self - .command_encoder_copy_buffer_to_texture::(encoder, &src, &dst, &size) + .command_encoder_copy_buffer_to_texture::(encoder, &src, &dst, &size) .unwrap(), trace::Command::CopyTextureToBuffer { src, dst, size } => self - .command_encoder_copy_texture_to_buffer::(encoder, &src, &dst, &size) + .command_encoder_copy_texture_to_buffer::(encoder, &src, &dst, &size) .unwrap(), trace::Command::CopyTextureToTexture { src, dst, size } => self - .command_encoder_copy_texture_to_texture::(encoder, &src, &dst, &size) + .command_encoder_copy_texture_to_texture::(encoder, &src, &dst, &size) .unwrap(), trace::Command::ClearBuffer { dst, offset, size } => self - .command_encoder_clear_buffer::(encoder, dst, offset, size) + .command_encoder_clear_buffer::(encoder, dst, offset, size) .unwrap(), trace::Command::ClearImage { dst, subresource_range, } => self - .command_encoder_clear_image::(encoder, dst, &subresource_range) + .command_encoder_clear_image::(encoder, dst, &subresource_range) .unwrap(), trace::Command::WriteTimestamp { query_set_id, query_index, } => self - .command_encoder_write_timestamp::(encoder, query_set_id, query_index) + .command_encoder_write_timestamp::(encoder, query_set_id, query_index) .unwrap(), trace::Command::ResolveQuerySet { query_set_id, @@ -103,7 +103,7 @@ impl GlobalPlay for wgc::hub::Global { destination, destination_offset, } => self - .command_encoder_resolve_query_set::( + .command_encoder_resolve_query_set::( encoder, query_set_id, start_query, @@ -113,7 +113,7 @@ impl GlobalPlay for wgc::hub::Global { ) .unwrap(), trace::Command::RunComputePass { base } => { - self.command_encoder_run_compute_pass_impl::(encoder, base.as_ref()) + self.command_encoder_run_compute_pass_impl::(encoder, base.as_ref()) .unwrap(); } trace::Command::RunRenderPass { @@ -121,7 +121,7 @@ impl GlobalPlay for wgc::hub::Global { target_colors, target_depth_stencil, } => { - self.command_encoder_run_render_pass_impl::( + self.command_encoder_run_render_pass_impl::( encoder, base.as_ref(), &target_colors, @@ -132,115 +132,117 @@ impl GlobalPlay for wgc::hub::Global { } } let (cmd_buf, error) = self - .command_encoder_finish::(encoder, &wgt::CommandBufferDescriptor { label: None }); + .command_encoder_finish::(encoder, &wgt::CommandBufferDescriptor { label: None }); if let Some(e) = error { panic!("{:?}", e); } cmd_buf } - fn process( + fn process( &self, device: wgc::id::DeviceId, action: trace::Action, dir: &Path, comb_manager: &mut wgc::hub::IdentityManager, ) { - use wgc::device::trace::Action as A; + use wgc::device::trace::Action; log::info!("action {:?}", action); //TODO: find a way to force ID perishing without excessive `maintain()` calls. match action { - A::Init { .. } => panic!("Unexpected Action::Init: has to be the first action only"), - A::CreateSwapChain { .. } | A::PresentSwapChain(_) => { + Action::Init { .. } => { + panic!("Unexpected Action::Init: has to be the first action only") + } + Action::CreateSwapChain { .. } | Action::PresentSwapChain(_) => { panic!("Unexpected SwapChain action: winit feature is not enabled") } - A::CreateBuffer(id, desc) => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.device_create_buffer::(device, &desc, id); + Action::CreateBuffer(id, desc) => { + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.device_create_buffer::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::FreeBuffer(id) => { - self.buffer_destroy::(id).unwrap(); + Action::FreeBuffer(id) => { + self.buffer_destroy::(id).unwrap(); } - A::DestroyBuffer(id) => { - self.buffer_drop::(id, true); + Action::DestroyBuffer(id) => { + self.buffer_drop::(id, true); } - A::CreateTexture(id, desc) => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.device_create_texture::(device, &desc, id); + Action::CreateTexture(id, desc) => { + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.device_create_texture::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::FreeTexture(id) => { - self.texture_destroy::(id).unwrap(); + Action::FreeTexture(id) => { + self.texture_destroy::(id).unwrap(); } - A::DestroyTexture(id) => { - self.texture_drop::(id, true); + Action::DestroyTexture(id) => { + self.texture_drop::(id, true); } - A::CreateTextureView { + Action::CreateTextureView { id, parent_id, desc, } => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.texture_create_view::(parent_id, &desc, id); + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.texture_create_view::(parent_id, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyTextureView(id) => { - self.texture_view_drop::(id, true).unwrap(); + Action::DestroyTextureView(id) => { + self.texture_view_drop::(id, true).unwrap(); } - A::CreateSampler(id, desc) => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.device_create_sampler::(device, &desc, id); + Action::CreateSampler(id, desc) => { + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.device_create_sampler::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroySampler(id) => { - self.sampler_drop::(id); + Action::DestroySampler(id) => { + self.sampler_drop::(id); } - A::GetSwapChainTexture { id, parent_id } => { - self.device_maintain_ids::(device).unwrap(); - self.swap_chain_get_current_texture_view::(parent_id, id) + Action::GetSwapChainTexture { id, parent_id } => { + self.device_maintain_ids::(device).unwrap(); + self.swap_chain_get_current_texture_view::(parent_id, id) .unwrap() .view_id .unwrap(); } - A::CreateBindGroupLayout(id, desc) => { - let (_, error) = self.device_create_bind_group_layout::(device, &desc, id); + Action::CreateBindGroupLayout(id, desc) => { + let (_, error) = self.device_create_bind_group_layout::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyBindGroupLayout(id) => { - self.bind_group_layout_drop::(id); + Action::DestroyBindGroupLayout(id) => { + self.bind_group_layout_drop::(id); } - A::CreatePipelineLayout(id, desc) => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.device_create_pipeline_layout::(device, &desc, id); + Action::CreatePipelineLayout(id, desc) => { + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.device_create_pipeline_layout::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyPipelineLayout(id) => { - self.pipeline_layout_drop::(id); + Action::DestroyPipelineLayout(id) => { + self.pipeline_layout_drop::(id); } - A::CreateBindGroup(id, desc) => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.device_create_bind_group::(device, &desc, id); + Action::CreateBindGroup(id, desc) => { + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.device_create_bind_group::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyBindGroup(id) => { - self.bind_group_drop::(id); + Action::DestroyBindGroup(id) => { + self.bind_group_drop::(id); } - A::CreateShaderModule { id, desc, data } => { + Action::CreateShaderModule { id, desc, data } => { let source = if data.ends_with(".wgsl") { let code = fs::read_to_string(dir.join(data)).unwrap(); wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code)) @@ -253,20 +255,20 @@ impl GlobalPlay for wgc::hub::Global { .collect::>(); wgc::pipeline::ShaderModuleSource::SpirV(Cow::Owned(spv)) }; - let (_, error) = self.device_create_shader_module::(device, &desc, source, id); + let (_, error) = self.device_create_shader_module::(device, &desc, source, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyShaderModule(id) => { - self.shader_module_drop::(id); + Action::DestroyShaderModule(id) => { + self.shader_module_drop::(id); } - A::CreateComputePipeline { + Action::CreateComputePipeline { id, desc, implicit_context, } => { - self.device_maintain_ids::(device).unwrap(); + self.device_maintain_ids::(device).unwrap(); let implicit_ids = implicit_context .as_ref() @@ -275,20 +277,20 @@ impl GlobalPlay for wgc::hub::Global { group_ids: &ic.group_ids, }); let (_, error) = - self.device_create_compute_pipeline::(device, &desc, id, implicit_ids); + self.device_create_compute_pipeline::(device, &desc, id, implicit_ids); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyComputePipeline(id) => { - self.compute_pipeline_drop::(id); + Action::DestroyComputePipeline(id) => { + self.compute_pipeline_drop::(id); } - A::CreateRenderPipeline { + Action::CreateRenderPipeline { id, desc, implicit_context, } => { - self.device_maintain_ids::(device).unwrap(); + self.device_maintain_ids::(device).unwrap(); let implicit_ids = implicit_context .as_ref() @@ -297,18 +299,18 @@ impl GlobalPlay for wgc::hub::Global { group_ids: &ic.group_ids, }); let (_, error) = - self.device_create_render_pipeline::(device, &desc, id, implicit_ids); + self.device_create_render_pipeline::(device, &desc, id, implicit_ids); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyRenderPipeline(id) => { - self.render_pipeline_drop::(id); + Action::DestroyRenderPipeline(id) => { + self.render_pipeline_drop::(id); } - A::CreateRenderBundle { id, desc, base } => { + Action::CreateRenderBundle { id, desc, base } => { let bundle = wgc::command::RenderBundleEncoder::new(&desc, device, Some(base)).unwrap(); - let (_, error) = self.render_bundle_encoder_finish::( + let (_, error) = self.render_bundle_encoder_finish::( bundle, &wgt::RenderBundleDescriptor { label: desc.label }, id, @@ -317,20 +319,20 @@ impl GlobalPlay for wgc::hub::Global { panic!("{:?}", e); } } - A::DestroyRenderBundle(id) => { - self.render_bundle_drop::(id); + Action::DestroyRenderBundle(id) => { + self.render_bundle_drop::(id); } - A::CreateQuerySet { id, desc } => { - self.device_maintain_ids::(device).unwrap(); - let (_, error) = self.device_create_query_set::(device, &desc, id); + Action::CreateQuerySet { id, desc } => { + self.device_maintain_ids::(device).unwrap(); + let (_, error) = self.device_create_query_set::(device, &desc, id); if let Some(e) = error { panic!("{:?}", e); } } - A::DestroyQuerySet(id) => { - self.query_set_drop::(id); + Action::DestroyQuerySet(id) => { + self.query_set_drop::(id); } - A::WriteBuffer { + Action::WriteBuffer { id, data, range, @@ -339,29 +341,29 @@ impl GlobalPlay for wgc::hub::Global { let bin = std::fs::read(dir.join(data)).unwrap(); let size = (range.end - range.start) as usize; if queued { - self.queue_write_buffer::(device, id, range.start, &bin) + self.queue_write_buffer::(device, id, range.start, &bin) .unwrap(); } else { - self.device_wait_for_buffer::(device, id).unwrap(); - self.device_set_buffer_sub_data::(device, id, range.start, &bin[..size]) + self.device_wait_for_buffer::(device, id).unwrap(); + self.device_set_buffer_sub_data::(device, id, range.start, &bin[..size]) .unwrap(); } } - A::WriteTexture { + Action::WriteTexture { to, data, layout, size, } => { let bin = std::fs::read(dir.join(data)).unwrap(); - self.queue_write_texture::(device, &to, &bin, &layout, &size) + self.queue_write_texture::(device, &to, &bin, &layout, &size) .unwrap(); } - A::Submit(_index, ref commands) if commands.is_empty() => { - self.queue_submit::(device, &[]).unwrap(); + Action::Submit(_index, ref commands) if commands.is_empty() => { + self.queue_submit::(device, &[]).unwrap(); } - A::Submit(_index, commands) => { - let (encoder, error) = self.device_create_command_encoder::( + Action::Submit(_index, commands) => { + let (encoder, error) = self.device_create_command_encoder::( device, &wgt::CommandEncoderDescriptor { label: None }, comb_manager.alloc(device.backend()), @@ -369,8 +371,8 @@ impl GlobalPlay for wgc::hub::Global { if let Some(e) = error { panic!("{:?}", e); } - let cmdbuf = self.encode_commands::(encoder, commands); - self.queue_submit::(device, &[cmdbuf]).unwrap(); + let cmdbuf = self.encode_commands::(encoder, commands); + self.queue_submit::(device, &[cmdbuf]).unwrap(); } } } diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index 1772bd4d0a..c2e79cd631 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -13,8 +13,6 @@ license = "MPL-2.0" [features] default = [] -# Enable SPIRV-Cross -cross = ["gfx-backend-metal/cross", "gfx-backend-gl/cross"] # Enable API tracing trace = ["ron", "serde", "wgt/trace", "arrayvec/serde"] # Enable API replaying @@ -36,38 +34,31 @@ serde = { version = "1.0", features = ["serde_derive"], optional = true } smallvec = "1" thiserror = "1" -gpu-alloc = "0.4" -gpu-descriptor = "0.1" - -hal = { package = "gfx-hal", git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } -gfx-backend-empty = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } - -[target.'cfg(all(not(target_arch = "wasm32"), all(unix, not(target_os = "ios"), not(target_os = "macos"))))'.dependencies] -gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521", features = ["naga"] } -gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } - -[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies] -gfx-backend-metal = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } -#TODO: could also depend on gfx-backend-vulkan for Vulkan Portability - -[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies] -gfx-backend-dx12 = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } -gfx-backend-dx11 = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } -gfx-backend-vulkan = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521", features = ["naga"] } - -[target.'cfg(target_arch = "wasm32")'.dependencies] -gfx-backend-gl = { git = "https://github.com/gfx-rs/gfx", rev = "27a1dae3796d33d23812f2bb8c7e3b5aea18b521" } - [dependencies.naga] git = "https://github.com/gfx-rs/naga" tag = "gfx-25" -features = ["spv-in", "spv-out", "wgsl-in"] +features = ["spv-in", "wgsl-in"] [dependencies.wgt] path = "../wgpu-types" package = "wgpu-types" version = "0.8" +[dependencies.hal] +path = "../wgpu-hal" +package = "wgpu-hal" +version = "0.1" + +[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies] +hal = { path = "../wgpu-hal", package = "wgpu-hal", 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 = { path = "../wgpu-hal", package = "wgpu-hal", features = ["vulkan"] } + +[target.'cfg(all(not(target_arch = "wasm32"), windows))'.dependencies] +hal = { path = "../wgpu-hal", package = "wgpu-hal", features = ["vulkan"] } + [dev-dependencies] loom = "0.3" diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs index 786064d70d..f1079ab715 100644 --- a/wgpu-core/build.rs +++ b/wgpu-core/build.rs @@ -13,8 +13,8 @@ fn main() { // Backends vulkan: { all(not(wasm), any(windows, unix_wo_apple)) }, metal: { all(not(wasm), apple) }, - dx12: { all(not(wasm), windows) }, - dx11: { all(not(wasm), windows) }, - gl: { any(wasm, unix_wo_apple) }, + dx12: { all(false, not(wasm), windows) }, + dx11: { all(false, not(wasm), windows) }, + gl: { all(false, any(wasm, unix_wo_apple)) }, } } diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 0a4fd65d63..902227b968 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -3,16 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - device::{ - descriptor::{DescriptorSet, DescriptorTotalCount}, - DeviceError, MissingFeatures, SHADER_STAGE_COUNT, - }, + device::{DeviceError, MissingFeatures, SHADER_STAGE_COUNT}, hub::Resource, id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid}, memory_init_tracker::MemoryInitTrackerAction, track::{TrackerSet, UsageConflict, DUMMY_SELECTOR}, validation::{MissingBufferUsageError, MissingTextureUsageError}, - FastHashMap, Label, LifeGuard, MultiRefCount, Stored, MAX_BIND_GROUPS, + FastHashMap, Label, LifeGuard, MultiRefCount, Stored, }; use arrayvec::ArrayVec; @@ -389,19 +386,18 @@ pub struct BindGroupLayoutDescriptor<'a> { pub(crate) type BindEntryMap = FastHashMap; #[derive(Debug)] -pub struct BindGroupLayout { - pub(crate) raw: B::DescriptorSetLayout, +pub struct BindGroupLayout { + pub(crate) raw: A::BindGroupLayout, pub(crate) device_id: Stored, pub(crate) multi_ref_count: MultiRefCount, pub(crate) entries: BindEntryMap, - pub(crate) desc_count: DescriptorTotalCount, pub(crate) dynamic_count: usize, pub(crate) count_validator: BindingTypeMaxCountValidator, #[cfg(debug_assertions)] pub(crate) label: String, } -impl Resource for BindGroupLayout { +impl Resource for BindGroupLayout { const TYPE: &'static str = "BindGroupLayout"; fn life_guard(&self) -> &LifeGuard { @@ -498,15 +494,15 @@ pub struct PipelineLayoutDescriptor<'a> { } #[derive(Debug)] -pub struct PipelineLayout { - pub(crate) raw: B::PipelineLayout, +pub struct PipelineLayout { + pub(crate) raw: A::PipelineLayout, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, - pub(crate) bind_group_layout_ids: ArrayVec<[Valid; MAX_BIND_GROUPS]>, + pub(crate) bind_group_layout_ids: ArrayVec<[Valid; hal::MAX_BIND_GROUPS]>, pub(crate) push_constant_ranges: ArrayVec<[wgt::PushConstantRange; SHADER_STAGE_COUNT]>, } -impl PipelineLayout { +impl PipelineLayout { /// Validate push constants match up with expected ranges. pub(crate) fn validate_push_constant_ranges( &self, @@ -586,7 +582,7 @@ impl PipelineLayout { } } -impl Resource for PipelineLayout { +impl Resource for PipelineLayout { const TYPE: &'static str = "PipelineLayout"; fn life_guard(&self) -> &LifeGuard { @@ -636,8 +632,8 @@ pub struct BindGroupDynamicBindingData { } #[derive(Debug)] -pub struct BindGroup { - pub(crate) raw: DescriptorSet, +pub struct BindGroup { + pub(crate) raw: A::BindGroup, pub(crate) device_id: Stored, pub(crate) layout_id: Valid, pub(crate) life_guard: LifeGuard, @@ -646,7 +642,7 @@ pub struct BindGroup { pub(crate) dynamic_binding_info: Vec, } -impl BindGroup { +impl BindGroup { pub(crate) fn validate_dynamic_bindings( &self, offsets: &[wgt::DynamicOffset], @@ -681,13 +677,13 @@ impl BindGroup { } } -impl Borrow<()> for BindGroup { +impl Borrow<()> for BindGroup { fn borrow(&self) -> &() { &DUMMY_SELECTOR } } -impl Resource for BindGroup { +impl Resource for BindGroup { const TYPE: &'static str = "BindGroup"; fn life_guard(&self) -> &LifeGuard { diff --git a/wgpu-core/src/command/allocator.rs b/wgpu-core/src/command/allocator.rs deleted file mode 100644 index 14b3507ef3..0000000000 --- a/wgpu-core/src/command/allocator.rs +++ /dev/null @@ -1,287 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use super::{CommandBuffer, CommandEncoderStatus}; -use crate::{ - device::DeviceError, hub::GfxBackend, id::DeviceId, track::TrackerSet, FastHashMap, - PrivateFeatures, Stored, SubmissionIndex, -}; - -#[cfg(debug_assertions)] -use crate::LabelHelpers; - -use hal::{command::CommandBuffer as _, device::Device as _, pool::CommandPool as _}; -use parking_lot::Mutex; -use thiserror::Error; - -use std::thread; - -const GROW_AMOUNT: usize = 20; - -#[derive(Debug)] -struct CommandPool { - raw: B::CommandPool, - total: usize, - available: Vec, - pending: Vec<(B::CommandBuffer, SubmissionIndex)>, -} - -impl CommandPool { - fn maintain(&mut self, last_done_index: SubmissionIndex) { - for i in (0..self.pending.len()).rev() { - if self.pending[i].1 <= last_done_index { - let (cmd_buf, index) = self.pending.swap_remove(i); - log::trace!( - "recycling cmdbuf submitted in {} when {} is last done", - index, - last_done_index, - ); - self.recycle(cmd_buf); - } - } - } - - fn recycle(&mut self, mut raw: B::CommandBuffer) { - unsafe { - raw.reset(false); - } - self.available.push(raw); - } - - fn allocate(&mut self) -> B::CommandBuffer { - if self.available.is_empty() { - self.total += GROW_AMOUNT; - unsafe { - self.raw.allocate( - GROW_AMOUNT, - hal::command::Level::Primary, - &mut self.available, - ) - }; - } - self.available.pop().unwrap() - } - - fn destroy(mut self, device: &B::Device) { - unsafe { - self.raw.free(self.available.into_iter()); - device.destroy_command_pool(self.raw); - } - } -} - -#[derive(Debug)] -struct Inner { - pools: FastHashMap>, -} - -#[derive(Debug)] -pub struct CommandAllocator { - queue_family: hal::queue::QueueFamilyId, - internal_thread_id: thread::ThreadId, - inner: Mutex>, -} - -impl CommandAllocator { - #[allow(clippy::too_many_arguments)] - pub(crate) fn allocate( - &self, - device_id: Stored, - device: &B::Device, - limits: wgt::Limits, - downlevel: wgt::DownlevelProperties, - features: wgt::Features, - private_features: PrivateFeatures, - label: &crate::Label, - #[cfg(feature = "trace")] enable_tracing: bool, - ) -> Result, CommandAllocatorError> { - //debug_assert_eq!(device_id.backend(), B::VARIANT); - let thread_id = thread::current().id(); - let mut inner = self.inner.lock(); - - use std::collections::hash_map::Entry; - let pool = match inner.pools.entry(thread_id) { - Entry::Vacant(e) => { - log::info!("Starting on thread {:?}", thread_id); - let raw = unsafe { - device - .create_command_pool( - self.queue_family, - hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL, - ) - .or(Err(DeviceError::OutOfMemory))? - }; - e.insert(CommandPool { - raw, - total: 0, - available: Vec::new(), - pending: Vec::new(), - }) - } - Entry::Occupied(e) => e.into_mut(), - }; - - //Note: we have to allocate the first buffer right here, or otherwise - // the pool may be cleaned up by maintenance called from another thread. - - Ok(CommandBuffer { - raw: vec![pool.allocate()], - status: CommandEncoderStatus::Recording, - recorded_thread_id: thread_id, - device_id, - trackers: TrackerSet::new(B::VARIANT), - used_swap_chains: Default::default(), - buffer_memory_init_actions: Default::default(), - limits, - downlevel, - private_features, - support_fill_buffer_texture: features.contains(wgt::Features::CLEAR_COMMANDS), - has_labels: label.is_some(), - #[cfg(feature = "trace")] - commands: if enable_tracing { - Some(Vec::new()) - } else { - None - }, - #[cfg(debug_assertions)] - label: label.to_string_or_default(), - }) - } -} - -impl CommandAllocator { - pub fn new( - queue_family: hal::queue::QueueFamilyId, - device: &B::Device, - ) -> Result { - let internal_thread_id = thread::current().id(); - log::info!("Starting on (internal) thread {:?}", internal_thread_id); - let mut pools = FastHashMap::default(); - pools.insert( - internal_thread_id, - CommandPool { - raw: unsafe { - device - .create_command_pool( - queue_family, - hal::pool::CommandPoolCreateFlags::RESET_INDIVIDUAL, - ) - .or(Err(DeviceError::OutOfMemory))? - }, - total: 0, - available: Vec::new(), - pending: Vec::new(), - }, - ); - Ok(Self { - queue_family, - internal_thread_id, - inner: Mutex::new(Inner { pools }), - }) - } - - fn allocate_for_thread_id(&self, thread_id: thread::ThreadId) -> B::CommandBuffer { - let mut inner = self.inner.lock(); - inner.pools.get_mut(&thread_id).unwrap().allocate() - } - - pub fn allocate_internal(&self) -> B::CommandBuffer { - self.allocate_for_thread_id(self.internal_thread_id) - } - - pub fn extend(&self, cmd_buf: &CommandBuffer) -> B::CommandBuffer { - self.allocate_for_thread_id(cmd_buf.recorded_thread_id) - } - - pub fn discard_internal(&self, raw: B::CommandBuffer) { - let mut inner = self.inner.lock(); - inner - .pools - .get_mut(&self.internal_thread_id) - .unwrap() - .recycle(raw); - } - - pub fn discard(&self, mut cmd_buf: CommandBuffer) { - cmd_buf.trackers.clear(); - let mut inner = self.inner.lock(); - let pool = inner.pools.get_mut(&cmd_buf.recorded_thread_id).unwrap(); - for raw in cmd_buf.raw { - pool.recycle(raw); - } - } - - pub fn after_submit_internal(&self, raw: B::CommandBuffer, submit_index: SubmissionIndex) { - let mut inner = self.inner.lock(); - inner - .pools - .get_mut(&self.internal_thread_id) - .unwrap() - .pending - .push((raw, submit_index)); - } - - pub fn after_submit( - &self, - cmd_buf: CommandBuffer, - device: &B::Device, - submit_index: SubmissionIndex, - ) { - // Record this command buffer as pending - let mut inner = self.inner.lock(); - let clear_label = cmd_buf.has_labels; - inner - .pools - .get_mut(&cmd_buf.recorded_thread_id) - .unwrap() - .pending - .extend(cmd_buf.raw.into_iter().map(|mut raw| { - if clear_label { - unsafe { device.set_command_buffer_name(&mut raw, "") }; - } - (raw, submit_index) - })); - } - - pub fn maintain(&self, device: &B::Device, last_done_index: SubmissionIndex) { - profiling::scope!("maintain", "CommandAllocator"); - let mut inner = self.inner.lock(); - let mut remove_threads = Vec::new(); - for (&thread_id, pool) in inner.pools.iter_mut() { - pool.maintain(last_done_index); - if pool.total == pool.available.len() && thread_id != self.internal_thread_id { - assert!(pool.pending.is_empty()); - remove_threads.push(thread_id); - } - } - for thread_id in remove_threads { - log::info!("Removing from thread {:?}", thread_id); - let pool = inner.pools.remove(&thread_id).unwrap(); - pool.destroy(device); - } - } - - pub fn destroy(self, device: &B::Device) { - let mut inner = self.inner.lock(); - for (_, mut pool) in inner.pools.drain() { - while let Some((raw, _)) = pool.pending.pop() { - pool.recycle(raw); - } - if pool.total != pool.available.len() { - log::error!( - "Some command buffers are still recorded, only tracking {} / {}", - pool.available.len(), - pool.total - ); - } - pool.destroy(device); - } - } -} - -#[derive(Clone, Debug, Error)] -pub enum CommandAllocatorError { - #[error(transparent)] - Device(#[from] DeviceError), -} diff --git a/wgpu-core/src/command/bind.rs b/wgpu-core/src/command/bind.rs index f24d0b96dd..43d73ce45c 100644 --- a/wgpu-core/src/command/bind.rs +++ b/wgpu-core/src/command/bind.rs @@ -5,9 +5,9 @@ use crate::{ binding_model::{BindGroup, PipelineLayout}, device::SHADER_STAGE_COUNT, - hub::{GfxBackend, Storage}, + hub::{HalApi, Storage}, id::{BindGroupId, BindGroupLayoutId, PipelineLayoutId, Valid}, - Stored, MAX_BIND_GROUPS, + Stored, }; use arrayvec::ArrayVec; @@ -42,7 +42,7 @@ mod compat { #[derive(Debug)] pub struct Manager { - entries: [Entry; crate::MAX_BIND_GROUPS], + entries: [Entry; hal::MAX_BIND_GROUPS], } impl Manager { @@ -145,7 +145,7 @@ pub(super) struct EntryPayload { pub(super) struct Binder { pub(super) pipeline_layout_id: Option>, //TODO: strongly `Stored` manager: compat::Manager>, - payloads: [EntryPayload; MAX_BIND_GROUPS], + payloads: [EntryPayload; hal::MAX_BIND_GROUPS], } impl Binder { @@ -166,9 +166,9 @@ impl Binder { } } - pub(super) fn change_pipeline_layout<'a, B: GfxBackend>( + pub(super) fn change_pipeline_layout<'a, A: HalApi>( &'a mut self, - guard: &Storage, PipelineLayoutId>, + guard: &Storage, PipelineLayoutId>, new_id: Valid, ) -> (usize, &'a [EntryPayload]) { let old_id_opt = self.pipeline_layout_id.replace(new_id); @@ -187,15 +187,15 @@ impl Binder { (bind_range.start, &self.payloads[bind_range]) } - pub(super) fn assign_group<'a, B: GfxBackend>( + pub(super) fn assign_group<'a, A: HalApi>( &'a mut self, index: usize, bind_group_id: Valid, - bind_group: &BindGroup, + bind_group: &BindGroup, offsets: &[wgt::DynamicOffset], ) -> &'a [EntryPayload] { log::trace!("\tBinding [{}] = group {:?}", index, bind_group_id); - debug_assert_eq!(B::VARIANT, bind_group_id.0.backend()); + debug_assert_eq!(A::VARIANT, bind_group_id.0.backend()); let payload = &mut self.payloads[index]; payload.group_id = Some(Stored { diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index ea25c6efdf..f9e07c8a04 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -43,22 +43,20 @@ use crate::{ StateChange, }, conv, - device::{ - AttachmentData, Device, DeviceError, RenderPassContext, MAX_VERTEX_BUFFERS, - SHADER_STAGE_COUNT, - }, - hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Resource, Storage, Token}, + device::{AttachmentData, Device, DeviceError, RenderPassContext, SHADER_STAGE_COUNT}, + hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Resource, Storage, Token}, id, memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, - resource::BufferUse, track::{TrackerSet, UsageConflict}, validation::check_buffer_usage, - Label, LabelHelpers, LifeGuard, Stored, MAX_BIND_GROUPS, + Label, LabelHelpers, LifeGuard, Stored, }; use arrayvec::ArrayVec; -use std::{borrow::Cow, iter, mem, ops::Range}; +use std::{borrow::Cow, mem, ops::Range}; use thiserror::Error; +use hal::CommandEncoder as _; + /// Describes a [`RenderBundleEncoder`]. #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] @@ -105,7 +103,7 @@ impl RenderBundleEncoder { if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) { return Err(CreateRenderBundleError::InvalidSampleCount(sc)); } - sc as u8 + sc }, }, }) @@ -135,12 +133,12 @@ impl RenderBundleEncoder { self.parent_id } - pub(crate) fn finish( + pub(crate) fn finish( self, desc: &RenderBundleDescriptor, - device: &Device, - hub: &Hub, - token: &mut Token>, + device: &Device, + hub: &Hub, + token: &mut Token>, ) -> Result { let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(token); let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); @@ -150,10 +148,12 @@ impl RenderBundleEncoder { let mut state = State { trackers: TrackerSet::new(self.parent_id.backend()), index: IndexState::new(), - vertex: (0..MAX_VERTEX_BUFFERS) + vertex: (0..hal::MAX_VERTEX_BUFFERS) .map(|_| VertexState::new()) .collect(), - bind: (0..MAX_BIND_GROUPS).map(|_| BindState::new()).collect(), + bind: (0..hal::MAX_BIND_GROUPS) + .map(|_| BindState::new()) + .collect(), push_constant_ranges: PushConstantState::new(), raw_dynamic_offsets: Vec::new(), flat_dynamic_offsets: Vec::new(), @@ -260,7 +260,7 @@ impl RenderBundleEncoder { let buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDEX) .unwrap(); check_buffer_usage(buffer.usage, wgt::BufferUsage::INDEX) .map_pass_err(scope)?; @@ -287,7 +287,7 @@ impl RenderBundleEncoder { let buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::VERTEX) .unwrap(); check_buffer_usage(buffer.usage, wgt::BufferUsage::VERTEX) .map_pass_err(scope)?; @@ -408,7 +408,7 @@ impl RenderBundleEncoder { let buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDIRECT) .unwrap(); check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT) .map_pass_err(scope)?; @@ -444,7 +444,7 @@ impl RenderBundleEncoder { let buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDIRECT) .map_err(|err| RenderCommandError::Buffer(buffer_id, err)) .map_pass_err(scope)?; check_buffer_usage(buffer.usage, wgt::BufferUsage::INDIRECT) @@ -565,23 +565,21 @@ impl RenderBundle { /// Note that the function isn't expected to fail, generally. /// All the validation has already been done by this point. /// The only failure condition is if some of the used buffers are destroyed. - pub(crate) unsafe fn execute( + pub(crate) unsafe fn execute( &self, - cmd_buf: &mut B::CommandBuffer, + raw: &mut A::CommandEncoder, pipeline_layout_guard: &Storage< - crate::binding_model::PipelineLayout, + crate::binding_model::PipelineLayout, id::PipelineLayoutId, >, - bind_group_guard: &Storage, id::BindGroupId>, - pipeline_guard: &Storage, id::RenderPipelineId>, - buffer_guard: &Storage, id::BufferId>, + bind_group_guard: &Storage, id::BindGroupId>, + pipeline_guard: &Storage, id::RenderPipelineId>, + buffer_guard: &Storage, id::BufferId>, ) -> Result<(), ExecutionError> { - use hal::command::CommandBuffer as _; - let mut offsets = self.base.dynamic_offsets.as_slice(); let mut pipeline_layout_id = None::>; if let Some(ref label) = self.base.label { - cmd_buf.begin_debug_marker(label, 0); + raw.begin_debug_marker(label); } for command in self.base.commands.iter() { @@ -592,17 +590,17 @@ impl RenderBundle { bind_group_id, } => { let bind_group = bind_group_guard.get(bind_group_id).unwrap(); - cmd_buf.bind_graphics_descriptor_sets( + raw.set_bind_group( &pipeline_layout_guard[pipeline_layout_id.unwrap()].raw, - index as usize, - iter::once(bind_group.raw.raw()), - offsets.iter().take(num_dynamic_offsets as usize).cloned(), + index as u32, + &bind_group.raw, + &offsets[num_dynamic_offsets as usize..], ); offsets = &offsets[num_dynamic_offsets as usize..]; } RenderCommand::SetPipeline(pipeline_id) => { let pipeline = pipeline_guard.get(pipeline_id).unwrap(); - cmd_buf.bind_graphics_pipeline(&pipeline.raw); + raw.set_render_pipeline(&pipeline.raw); pipeline_layout_id = Some(pipeline.layout_id.value); } @@ -612,19 +610,18 @@ impl RenderBundle { offset, size, } => { - let index_type = conv::map_index_format(index_format); - - let &(ref buffer, _) = buffer_guard + let buffer = buffer_guard .get(buffer_id) .unwrap() .raw .as_ref() .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?; - let range = hal::buffer::SubRange { + let bb = hal::BufferBinding { + buffer, offset, - size: size.map(|s| s.get()), + size, }; - cmd_buf.bind_index_buffer(buffer, range, index_type); + raw.set_index_buffer(bb, index_format); } RenderCommand::SetVertexBuffer { slot, @@ -632,17 +629,18 @@ impl RenderBundle { offset, size, } => { - let &(ref buffer, _) = buffer_guard + let buffer = buffer_guard .get(buffer_id) .unwrap() .raw .as_ref() .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?; - let range = hal::buffer::SubRange { + let bb = hal::BufferBinding { + buffer, offset, - size: size.map(|s| s.get()), + size, }; - cmd_buf.bind_vertex_buffers(slot, iter::once((buffer, range))); + raw.set_vertex_buffer(slot, bb); } RenderCommand::SetPushConstant { stages, @@ -659,20 +657,15 @@ impl RenderBundle { let data_slice = &self.base.push_constant_data [(values_offset as usize)..values_end_offset]; - cmd_buf.push_graphics_constants( - &pipeline_layout.raw, - conv::map_shader_stage_flags(stages), - offset, - &data_slice, - ) + raw.set_push_constants(&pipeline_layout.raw, stages, offset, data_slice) } else { super::push_constant_clear( offset, size_bytes, |clear_offset, clear_data| { - cmd_buf.push_graphics_constants( + raw.set_push_constants( &pipeline_layout.raw, - conv::map_shader_stage_flags(stages), + stages, clear_offset, clear_data, ); @@ -686,10 +679,7 @@ impl RenderBundle { first_vertex, first_instance, } => { - cmd_buf.draw( - first_vertex..first_vertex + vertex_count, - first_instance..first_instance + instance_count, - ); + raw.draw(first_vertex, vertex_count, first_instance, instance_count); } RenderCommand::DrawIndexed { index_count, @@ -698,10 +688,12 @@ impl RenderBundle { base_vertex, first_instance, } => { - cmd_buf.draw_indexed( - first_index..first_index + index_count, + raw.draw_indexed( + first_index, + index_count, base_vertex, - first_instance..first_instance + instance_count, + first_instance, + instance_count, ); } RenderCommand::MultiDrawIndirect { @@ -710,13 +702,13 @@ impl RenderBundle { count: None, indexed: false, } => { - let &(ref buffer, _) = buffer_guard + let buffer = buffer_guard .get(buffer_id) .unwrap() .raw .as_ref() .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?; - cmd_buf.draw_indirect(buffer, offset, 1, 0); + raw.draw_indirect(buffer, offset, 1); } RenderCommand::MultiDrawIndirect { buffer_id, @@ -724,13 +716,13 @@ impl RenderBundle { count: None, indexed: true, } => { - let &(ref buffer, _) = buffer_guard + let buffer = buffer_guard .get(buffer_id) .unwrap() .raw .as_ref() .ok_or(ExecutionError::DestroyedBuffer(buffer_id))?; - cmd_buf.draw_indexed_indirect(buffer, offset, 1, 0); + raw.draw_indexed_indirect(buffer, offset, 1); } RenderCommand::MultiDrawIndirect { .. } | RenderCommand::MultiDrawIndirectCount { .. } => { @@ -755,7 +747,7 @@ impl RenderBundle { } if let Some(_) = self.base.label { - cmd_buf.end_debug_marker(); + raw.end_debug_marker(); } Ok(()) @@ -943,8 +935,8 @@ struct VertexLimitState { struct State { trackers: TrackerSet, index: IndexState, - vertex: ArrayVec<[VertexState; MAX_VERTEX_BUFFERS]>, - bind: ArrayVec<[BindState; MAX_BIND_GROUPS]>, + vertex: ArrayVec<[VertexState; hal::MAX_VERTEX_BUFFERS]>, + bind: ArrayVec<[BindState; hal::MAX_BIND_GROUPS]>, push_constant_ranges: PushConstantState, raw_dynamic_offsets: Vec, flat_dynamic_offsets: Vec, diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index 1698613dda..1f1274128b 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -8,16 +8,13 @@ use std::{num::NonZeroU32, ops::Range}; use crate::device::trace::Command as TraceCommand; use crate::{ command::CommandBuffer, - conv, - device::all_buffer_stages, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Token}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Token}, id::{BufferId, CommandEncoderId, TextureId}, memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, - resource::{BufferUse, TextureUse}, track::TextureSelector, }; -use hal::command::CommandBuffer as _; +use hal::CommandEncoder as _; use thiserror::Error; use wgt::{ BufferAddress, BufferSize, BufferUsage, ImageSubresourceRange, TextureAspect, TextureUsage, @@ -46,29 +43,29 @@ pub enum ClearError { }, #[error("destination buffer/texture is missing the `COPY_DST` usage flag")] MissingCopyDstUsageFlag(Option, Option), - #[error("texture lacks the aspects that were specified in the image subresource range. Texture has {texture_aspects:?}, specified was {subresource_range_aspects:?}")] + #[error("texture lacks the aspects that were specified in the image subresource range. Texture with format {texture_format:?}, specified was {subresource_range_aspects:?}")] MissingTextureAspect { - texture_aspects: hal::format::Aspects, + texture_format: wgt::TextureFormat, subresource_range_aspects: TextureAspect, }, #[error("image subresource level range is outside of the texture's level range. texture range is {texture_level_range:?}, \ whereas subesource range specified start {subresource_base_mip_level} and count {subresource_mip_level_count:?}")] InvalidTextureLevelRange { - texture_level_range: Range, + texture_level_range: Range, subresource_base_mip_level: u32, subresource_mip_level_count: Option, }, #[error("image subresource layer range is outside of the texture's layer range. texture range is {texture_layer_range:?}, \ whereas subesource range specified start {subresource_base_array_layer} and count {subresource_array_layer_count:?}")] InvalidTextureLayerRange { - texture_layer_range: Range, + texture_layer_range: Range, subresource_base_array_layer: u32, subresource_array_layer_count: Option, }, } impl Global { - pub fn command_encoder_clear_buffer( + pub fn command_encoder_clear_buffer( &self, command_encoder_id: CommandEncoderId, dst: BufferId, @@ -77,7 +74,7 @@ impl Global { ) -> Result<(), ClearError> { profiling::scope!("CommandEncoder::fill_buffer"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id) @@ -96,9 +93,9 @@ impl Global { let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, dst, (), BufferUse::COPY_DST) + .use_replace(&*buffer_guard, dst, (), hal::BufferUse::COPY_DST) .map_err(ClearError::InvalidBuffer)?; - let &(ref dst_raw, _) = dst_buffer + let dst_raw = dst_buffer .raw .as_ref() .ok_or(ClearError::InvalidBuffer(dst))?; @@ -124,8 +121,11 @@ impl Global { } } - let num_bytes_filled = size.map_or(dst_buffer.size - offset, |s| s.get()); - if num_bytes_filled == 0 { + let end = match size { + Some(size) => offset + size.get(), + None => dst_buffer.size, + }; + if offset == end { log::trace!("Ignoring fill_buffer of size 0"); return Ok(()); } @@ -134,7 +134,7 @@ impl Global { cmd_buf.buffer_memory_init_actions.extend( dst_buffer .initialization_status - .check(offset..(offset + num_bytes_filled)) + .check(offset..end) .map(|range| MemoryInitTrackerAction { id: dst, range, @@ -143,29 +143,16 @@ impl Global { ); // actual hal barrier & operation - let dst_barrier = dst_pending - .map(|pending| pending.into_hal(dst_buffer)) - .next(); - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.pipeline_barrier( - all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - dst_barrier.into_iter(), - ); - cmd_buf_raw.fill_buffer( - dst_raw, - hal::buffer::SubRange { - offset, - size: size.map(|s| s.get()), - }, - 0, - ); + cmd_buf_raw.transition_buffers(dst_barrier); + cmd_buf_raw.fill_buffer(dst_raw, offset..end, 0); } Ok(()) } - pub fn command_encoder_clear_image( + pub fn command_encoder_clear_image( &self, command_encoder_id: CommandEncoderId, dst: TextureId, @@ -173,7 +160,7 @@ impl Global { ) -> Result<(), ClearError> { profiling::scope!("CommandEncoder::clear_image"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id) @@ -198,24 +185,20 @@ impl Global { .map_err(|_| ClearError::InvalidTexture(dst))?; // Check if subresource aspects are valid. - let aspects = match subresource_range.aspect { - wgt::TextureAspect::All => dst_texture.aspects, - wgt::TextureAspect::DepthOnly => hal::format::Aspects::DEPTH, - wgt::TextureAspect::StencilOnly => hal::format::Aspects::STENCIL, - }; - if !dst_texture.aspects.contains(aspects) { + let requested_aspects = hal::FormatAspect::from(subresource_range.aspect); + let clear_aspects = hal::FormatAspect::from(dst_texture.desc.format) & requested_aspects; + if clear_aspects.is_empty() { return Err(ClearError::MissingTextureAspect { - texture_aspects: dst_texture.aspects, + texture_format: dst_texture.desc.format, subresource_range_aspects: subresource_range.aspect, }); }; // Check if subresource level range is valid - let subresource_level_end = if let Some(count) = subresource_range.mip_level_count { - (subresource_range.base_mip_level + count.get()) as u8 - } else { - dst_texture.full_range.levels.end + let subresource_level_end = match subresource_range.mip_level_count { + Some(count) => subresource_range.base_mip_level + count.get(), + None => dst_texture.full_range.levels.end, }; - if dst_texture.full_range.levels.start > subresource_range.base_mip_level as u8 + if dst_texture.full_range.levels.start > subresource_range.base_mip_level || dst_texture.full_range.levels.end < subresource_level_end { return Err(ClearError::InvalidTextureLevelRange { @@ -225,12 +208,11 @@ impl Global { }); } // Check if subresource layer range is valid - let subresource_layer_end = if let Some(count) = subresource_range.array_layer_count { - (subresource_range.base_array_layer + count.get()) as u16 - } else { - dst_texture.full_range.layers.end + let subresource_layer_end = match subresource_range.array_layer_count { + Some(count) => subresource_range.base_array_layer + count.get(), + None => dst_texture.full_range.layers.end, }; - if dst_texture.full_range.layers.start > subresource_range.base_array_layer as u16 + if dst_texture.full_range.layers.start > subresource_range.base_array_layer || dst_texture.full_range.layers.end < subresource_layer_end { return Err(ClearError::InvalidTextureLayerRange { @@ -248,31 +230,26 @@ impl Global { &*texture_guard, dst, TextureSelector { - levels: subresource_range.base_mip_level as u8..subresource_level_end, - layers: subresource_range.base_array_layer as u16..subresource_layer_end, + levels: subresource_range.base_mip_level..subresource_level_end, + layers: subresource_range.base_array_layer..subresource_layer_end, }, - TextureUse::COPY_DST, + hal::TextureUse::COPY_DST, ) .map_err(ClearError::InvalidTexture)?; - let &(ref dst_raw, _) = dst_texture + let _dst_raw = dst_texture .raw .as_ref() .ok_or(ClearError::InvalidTexture(dst))?; - if !dst_texture.usage.contains(TextureUsage::COPY_DST) { + if !dst_texture.desc.usage.contains(TextureUsage::COPY_DST) { return Err(ClearError::MissingCopyDstUsageFlag(None, Some(dst))); } // actual hal barrier & operation - let dst_barrier = dst_pending - .map(|pending| pending.into_hal(dst_texture)) - .next(); - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_texture)); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.pipeline_barrier( - all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - dst_barrier.into_iter(), - ); + cmd_buf_raw.transition_textures(dst_barrier); + /*TODO: image clears cmd_buf_raw.clear_image( dst_raw, hal::image::Layout::TransferDstOptimal, @@ -288,7 +265,7 @@ impl Global { layer_start: subresource_range.base_array_layer as u16, layer_count: subresource_range.array_layer_count.map(|c| c.get() as u16), }), - ); + );*/ } Ok(()) } diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 7a461d7c56..d3980100ff 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -9,20 +9,19 @@ use crate::{ CommandEncoderError, CommandEncoderStatus, MapPassErr, PassErrorScope, QueryUseError, StateChange, }, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, - resource::{Buffer, BufferUse, Texture}, - track::{StatefulTrackerSubset, TrackerSet, UsageConflict}, + resource::{Buffer, Texture}, + track::{StatefulTrackerSubset, TrackerSet, UsageConflict, UseExtendError}, validation::{check_buffer_usage, MissingBufferUsageError}, Label, DOWNLEVEL_ERROR_WARNING_MESSAGE, }; -use hal::command::CommandBuffer as _; +use hal::CommandEncoder as _; use thiserror::Error; use wgt::{BufferAddress, BufferUsage, ShaderStage}; -use crate::track::UseExtendError; use std::{fmt, mem, str}; #[doc(hidden)] @@ -214,13 +213,13 @@ impl State { Ok(()) } - fn flush_states( + fn flush_states( &mut self, - raw_cmd_buf: &mut B::CommandBuffer, + raw_encoder: &mut A::CommandEncoder, base_trackers: &mut TrackerSet, - bind_group_guard: &Storage, id::BindGroupId>, - buffer_guard: &Storage, id::BufferId>, - texture_guard: &Storage, id::TextureId>, + bind_group_guard: &Storage, id::BindGroupId>, + buffer_guard: &Storage, id::BufferId>, + texture_guard: &Storage, id::TextureId>, ) -> Result<(), UsageConflict> { for id in self.binder.list_active() { self.trackers.merge_extend(&bind_group_guard[id].used)?; @@ -230,7 +229,7 @@ impl State { log::trace!("Encoding dispatch barriers"); CommandBuffer::insert_barriers( - raw_cmd_buf, + raw_encoder, base_trackers, &self.trackers.buffers, &self.trackers.textures, @@ -246,16 +245,16 @@ impl State { // Common routines between render/compute impl Global { - pub fn command_encoder_run_compute_pass( + pub fn command_encoder_run_compute_pass( &self, encoder_id: id::CommandEncoderId, pass: &ComputePass, ) -> Result<(), ComputePassError> { - self.command_encoder_run_compute_pass_impl::(encoder_id, pass.base.as_ref()) + self.command_encoder_run_compute_pass_impl::(encoder_id, pass.base.as_ref()) } #[doc(hidden)] - pub fn command_encoder_run_compute_pass_impl( + pub fn command_encoder_run_compute_pass_impl( &self, encoder_id: id::CommandEncoderId, base: BasePassRef, @@ -263,7 +262,7 @@ impl Global { profiling::scope!("run_compute_pass", "CommandEncoder"); let scope = PassErrorScope::Pass(encoder_id); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); @@ -271,7 +270,7 @@ impl Global { CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, encoder_id).map_pass_err(scope)?; // will be reset to true if recording is done without errors cmd_buf.status = CommandEncoderStatus::Error; - let raw = cmd_buf.raw.last_mut().unwrap(); + let raw = cmd_buf.encoder.open(); #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -291,12 +290,6 @@ impl Global { }); } - if let Some(ref label) = base.label { - unsafe { - raw.begin_debug_marker(label, 0); - } - } - let (_, mut token) = hub.render_bundles.read(&mut token); let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); let (bind_group_guard, mut token) = hub.bind_groups.read(&mut token); @@ -308,7 +301,7 @@ impl Global { let mut state = State { binder: Binder::new(), pipeline: StateChange::new(), - trackers: StatefulTrackerSubset::new(B::VARIANT), + trackers: StatefulTrackerSubset::new(A::VARIANT), debug_scope_depth: 0, }; let mut temp_offsets = Vec::new(); @@ -316,6 +309,11 @@ impl Global { let mut string_offset = 0; let mut active_query = None; + let hal_desc = hal::ComputePassDescriptor { label: base.label }; + unsafe { + raw.begin_compute_pass(&hal_desc); + } + for command in base.commands { match *command { ComputeCommand::SetBindGroup { @@ -377,19 +375,16 @@ impl Global { if !entries.is_empty() { let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id.unwrap()].raw; - let desc_sets = entries.iter().map(|e| { - bind_group_guard[e.group_id.as_ref().unwrap().value] - .raw - .raw() - }); - let offsets = entries.iter().flat_map(|e| &e.dynamic_offsets).cloned(); - unsafe { - raw.bind_compute_descriptor_sets( - pipeline_layout, - index as usize, - desc_sets, - offsets, - ); + for (i, e) in entries.iter().enumerate() { + let raw_bg = &bind_group_guard[e.group_id.as_ref().unwrap().value].raw; + unsafe { + raw.set_bind_group( + pipeline_layout, + index as u32 + i as u32, + raw_bg, + &e.dynamic_offsets, + ); + } } } } @@ -408,7 +403,7 @@ impl Global { .map_pass_err(scope)?; unsafe { - raw.bind_compute_pipeline(&pipeline.raw); + raw.set_compute_pipeline(&pipeline.raw); } // Rebind resources @@ -420,19 +415,17 @@ impl Global { pipeline.layout_id.value, ); if !entries.is_empty() { - let desc_sets = entries.iter().map(|e| { - bind_group_guard[e.group_id.as_ref().unwrap().value] - .raw - .raw() - }); - let offsets = entries.iter().flat_map(|e| &e.dynamic_offsets).cloned(); - unsafe { - raw.bind_compute_descriptor_sets( - &pipeline_layout.raw, - start_index, - desc_sets, - offsets, - ); + for (i, e) in entries.iter().enumerate() { + let raw_bg = + &bind_group_guard[e.group_id.as_ref().unwrap().value].raw; + unsafe { + raw.set_bind_group( + &pipeline_layout.raw, + start_index as u32 + i as u32, + raw_bg, + &e.dynamic_offsets, + ); + } } } @@ -447,8 +440,9 @@ impl Global { offset, size_bytes, |clear_offset, clear_data| unsafe { - raw.push_compute_constants( + raw.set_push_constants( &pipeline_layout.raw, + wgt::ShaderStage::COMPUTE, clear_offset, clear_data, ); @@ -488,7 +482,14 @@ impl Global { ) .map_pass_err(scope)?; - unsafe { raw.push_compute_constants(&pipeline_layout.raw, offset, data_slice) } + unsafe { + raw.set_push_constants( + &pipeline_layout.raw, + wgt::ShaderStage::COMPUTE, + offset, + data_slice, + ); + } } ComputeCommand::Dispatch(groups) => { let scope = PassErrorScope::Dispatch { @@ -521,7 +522,7 @@ impl Global { let indirect_buffer = state .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDIRECT) .map_err(|_| ComputePassErrorInner::InvalidIndirectBuffer(buffer_id)) .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, BufferUsage::INDIRECT) @@ -537,7 +538,7 @@ impl Global { .map_pass_err(scope); } - let &(ref buf_raw, _) = indirect_buffer + let buf_raw = indirect_buffer .raw .as_ref() .ok_or(ComputePassErrorInner::InvalidIndirectBuffer(buffer_id)) @@ -569,14 +570,14 @@ impl Global { raw.dispatch_indirect(buf_raw, offset); } } - ComputeCommand::PushDebugGroup { color, len } => { + ComputeCommand::PushDebugGroup { color: _, len } => { state.debug_scope_depth += 1; let label = str::from_utf8(&base.string_data[string_offset..string_offset + len]) .unwrap(); string_offset += len; unsafe { - raw.begin_debug_marker(label, color); + raw.begin_debug_marker(label); } } ComputeCommand::PopDebugGroup => { @@ -591,12 +592,12 @@ impl Global { raw.end_debug_marker(); } } - ComputeCommand::InsertDebugMarker { color, len } => { + ComputeCommand::InsertDebugMarker { color: _, len } => { let label = str::from_utf8(&base.string_data[string_offset..string_offset + len]) .unwrap(); string_offset += len; - unsafe { raw.insert_debug_marker(label, color) } + unsafe { raw.insert_debug_marker(label) } } ComputeCommand::WriteTimestamp { query_set_id, @@ -657,10 +658,8 @@ impl Global { } } - if let Some(_) = base.label { - unsafe { - raw.end_debug_marker(); - } + unsafe { + raw.end_compute_pass(); } cmd_buf.status = CommandEncoderStatus::Recording; diff --git a/wgpu-core/src/command/draw.rs b/wgpu-core/src/command/draw.rs index 7c40603df9..8ea2c7b784 100644 --- a/wgpu-core/src/command/draw.rs +++ b/wgpu-core/src/command/draw.rs @@ -8,7 +8,6 @@ use crate::{ binding_model::PushConstantUploadError, id, - resource::BufferUse, track::UseExtendError, validation::{MissingBufferUsageError, MissingTextureUsageError}, }; @@ -17,7 +16,7 @@ use wgt::{BufferAddress, BufferSize, Color}; use std::num::NonZeroU32; use thiserror::Error; -pub type BufferError = UseExtendError; +pub type BufferError = UseExtendError; /// Error validating a draw call. #[derive(Clone, Debug, Error, PartialEq)] diff --git a/wgpu-core/src/command/mod.rs b/wgpu-core/src/command/mod.rs index 5564873e30..19f78acdd6 100644 --- a/wgpu-core/src/command/mod.rs +++ b/wgpu-core/src/command/mod.rs @@ -2,7 +2,6 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -mod allocator; mod bind; mod bundle; mod clear; @@ -12,8 +11,6 @@ mod query; mod render; mod transfer; -pub(crate) use self::allocator::CommandAllocator; -pub use self::allocator::CommandAllocatorError; pub use self::bundle::*; pub use self::compute::*; pub use self::draw::*; @@ -22,21 +19,18 @@ pub use self::render::*; pub use self::transfer::*; use crate::{ - device::{all_buffer_stages, all_image_stages}, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, memory_init_tracker::MemoryInitTrackerAction, resource::{Buffer, Texture}, track::{BufferState, ResourceTracker, TextureState, TrackerSet}, - Label, PrivateFeatures, Stored, + Label, Stored, }; -use hal::command::CommandBuffer as _; +use hal::CommandEncoder as _; use smallvec::SmallVec; use thiserror::Error; -use std::thread::ThreadId; - const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64]; #[derive(Debug)] @@ -46,60 +40,97 @@ enum CommandEncoderStatus { Error, } -#[derive(Debug)] -pub struct CommandBuffer { - pub(crate) raw: Vec, +struct CommandEncoder { + raw: A::CommandEncoder, + list: Vec, + is_open: bool, + label: Option, +} + +//TODO: handle errors better +impl CommandEncoder { + fn close(&mut self) { + if self.is_open { + self.is_open = false; + let cmd_buf = unsafe { self.raw.end_encoding().unwrap() }; + self.list.push(cmd_buf); + } + } + + fn open(&mut self) -> &mut A::CommandEncoder { + if !self.is_open { + self.is_open = true; + let label = self.label.as_deref(); + unsafe { self.raw.begin_encoding(label).unwrap() }; + } + &mut self.raw + } +} + +pub struct BackedCommands { + pub(crate) encoder: A::CommandEncoder, + pub(crate) list: Vec, + pub(crate) trackers: TrackerSet, +} + +pub struct CommandBuffer { + encoder: CommandEncoder, status: CommandEncoderStatus, - recorded_thread_id: ThreadId, pub(crate) device_id: Stored, pub(crate) trackers: TrackerSet, pub(crate) used_swap_chains: SmallVec<[Stored; 1]>, pub(crate) buffer_memory_init_actions: Vec>, limits: wgt::Limits, - downlevel: wgt::DownlevelProperties, - private_features: PrivateFeatures, + downlevel: wgt::DownlevelCapabilities, support_fill_buffer_texture: bool, - has_labels: bool, #[cfg(feature = "trace")] pub(crate) commands: Option>, - #[cfg(debug_assertions)] - pub(crate) label: String, } -impl CommandBuffer { - fn get_encoder_mut( - storage: &mut Storage, - id: id::CommandEncoderId, - ) -> Result<&mut Self, CommandEncoderError> { - match storage.get_mut(id) { - Ok(cmd_buf) => match cmd_buf.status { - CommandEncoderStatus::Recording => Ok(cmd_buf), - CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording), - CommandEncoderStatus::Error => Err(CommandEncoderError::Invalid), +impl CommandBuffer { + pub(crate) fn new( + encoder: A::CommandEncoder, + device_id: Stored, + limits: wgt::Limits, + downlevel: wgt::DownlevelCapabilities, + features: wgt::Features, + #[cfg(feature = "trace")] enable_tracing: bool, + label: &Label, + ) -> Self { + CommandBuffer { + encoder: CommandEncoder { + raw: encoder, + is_open: false, + list: Vec::new(), + label: crate::LabelHelpers::borrow_option(label).map(|s| s.to_string()), + }, + status: CommandEncoderStatus::Recording, + device_id, + trackers: TrackerSet::new(A::VARIANT), + used_swap_chains: Default::default(), + buffer_memory_init_actions: Default::default(), + limits, + downlevel, + support_fill_buffer_texture: features.contains(wgt::Features::CLEAR_COMMANDS), + #[cfg(feature = "trace")] + commands: if enable_tracing { + Some(Vec::new()) + } else { + None }, - Err(_) => Err(CommandEncoderError::Invalid), - } - } - - pub fn is_finished(&self) -> bool { - match self.status { - CommandEncoderStatus::Finished => true, - _ => false, } } pub(crate) fn insert_barriers( - raw: &mut B::CommandBuffer, + raw: &mut A::CommandEncoder, base: &mut TrackerSet, head_buffers: &ResourceTracker, head_textures: &ResourceTracker, - buffer_guard: &Storage, id::BufferId>, - texture_guard: &Storage, id::TextureId>, + buffer_guard: &Storage, id::BufferId>, + texture_guard: &Storage, id::TextureId>, ) { - use hal::command::CommandBuffer as _; - profiling::scope!("insert_barriers"); - debug_assert_eq!(B::VARIANT, base.backend()); + debug_assert_eq!(A::VARIANT, base.backend()); let buffer_barriers = base.buffers.merge_replace(head_buffers).map(|pending| { let buf = &buffer_guard[pending.id]; @@ -110,19 +141,45 @@ impl CommandBuffer { pending.into_hal(tex) }); - //TODO: be more deliberate about the stages - let stages = all_buffer_stages() | all_image_stages(); unsafe { - raw.pipeline_barrier( - stages..stages, - hal::memory::Dependencies::empty(), - buffer_barriers.chain(texture_barriers), - ); + raw.transition_buffers(buffer_barriers); + raw.transition_textures(texture_barriers); + } + } +} + +impl CommandBuffer { + fn get_encoder_mut( + storage: &mut Storage, + id: id::CommandEncoderId, + ) -> Result<&mut Self, CommandEncoderError> { + match storage.get_mut(id) { + Ok(cmd_buf) => match cmd_buf.status { + CommandEncoderStatus::Recording => Ok(cmd_buf), + CommandEncoderStatus::Finished => Err(CommandEncoderError::NotRecording), + CommandEncoderStatus::Error => Err(CommandEncoderError::Invalid), + }, + Err(_) => Err(CommandEncoderError::Invalid), + } + } + + pub fn is_finished(&self) -> bool { + match self.status { + CommandEncoderStatus::Finished => true, + _ => false, + } + } + + pub(crate) fn into_baked(self) -> BackedCommands { + BackedCommands { + encoder: self.encoder.raw, + list: self.encoder.list, + trackers: self.trackers, } } } -impl crate::hub::Resource for CommandBuffer { +impl crate::hub::Resource for CommandBuffer { const TYPE: &'static str = "CommandBuffer"; fn life_guard(&self) -> &crate::LifeGuard { @@ -130,10 +187,7 @@ impl crate::hub::Resource for CommandBuffer { } fn label(&self) -> &str { - #[cfg(debug_assertions)] - return &self.label; - #[cfg(not(debug_assertions))] - return ""; + self.encoder.label.as_ref().map_or("", |s| s.as_str()) } } @@ -206,30 +260,23 @@ pub enum CommandEncoderError { } impl Global { - pub fn command_encoder_finish( + pub fn command_encoder_finish( &self, encoder_id: id::CommandEncoderId, _desc: &wgt::CommandBufferDescriptor, } -impl QueryResetMap { +impl QueryResetMap { pub fn new() -> Self { Self { map: FastHashMap::default(), @@ -35,7 +34,7 @@ impl QueryResetMap { pub fn use_query_set( &mut self, id: id::QuerySetId, - query_set: &QuerySet, + query_set: &QuerySet, query: u32, ) -> bool { let (index, epoch, _) = id.unzip(); @@ -49,8 +48,8 @@ impl QueryResetMap { pub fn reset_queries( self, - cmd_buf_raw: &mut B::CommandBuffer, - query_set_storage: &Storage, id::QuerySetId>, + raw_encoder: &mut A::CommandEncoder, + query_set_storage: &Storage, id::QuerySetId>, backend: wgt::Backend, ) -> Result<(), id::QuerySetId> { for (query_set_id, (state, epoch)) in self.map.into_iter() { @@ -70,7 +69,7 @@ impl QueryResetMap { // We've hit the end of a run, dispatch a reset (Some(start), false) => { run_start = None; - unsafe { cmd_buf_raw.reset_query_pool(&query_set.raw, start..idx as u32) }; + unsafe { raw_encoder.reset_queries(&query_set.raw, start..idx as u32) }; } // We're starting a run (None, true) => { @@ -88,12 +87,14 @@ impl QueryResetMap { #[derive(Debug, Copy, Clone, PartialEq, Eq)] pub enum SimplifiedQueryType { + Occlusion, Timestamp, PipelineStatistics, } impl From for SimplifiedQueryType { fn from(q: wgt::QueryType) -> Self { match q { + wgt::QueryType::Occlusion => SimplifiedQueryType::Occlusion, wgt::QueryType::Timestamp => SimplifiedQueryType::Timestamp, wgt::QueryType::PipelineStatistics(..) => SimplifiedQueryType::PipelineStatistics, } @@ -161,14 +162,14 @@ pub enum ResolveError { }, } -impl QuerySet { +impl QuerySet { fn validate_query( &self, query_set_id: id::QuerySetId, query_type: SimplifiedQueryType, query_index: u32, - reset_state: Option<&mut QueryResetMap>, - ) -> Result, QueryUseError> { + reset_state: Option<&mut QueryResetMap>, + ) -> Result<&A::QuerySet, QueryUseError> { // We need to defer our resets because we are in a renderpass, add the usage to the reset map. if let Some(reset) = reset_state { let used = reset.use_query_set(query_set_id, self, query_index); @@ -192,23 +193,18 @@ impl QuerySet { }); } - let hal_query = hal::query::Query:: { - pool: &self.raw, - id: query_index, - }; - - Ok(hal_query) + Ok(&self.raw) } pub(super) fn validate_and_write_timestamp( &self, - cmd_buf_raw: &mut B::CommandBuffer, + raw_encoder: &mut A::CommandEncoder, query_set_id: id::QuerySetId, query_index: u32, - reset_state: Option<&mut QueryResetMap>, + reset_state: Option<&mut QueryResetMap>, ) -> Result<(), QueryUseError> { let needs_reset = reset_state.is_none(); - let hal_query = self.validate_query( + let query_set = self.validate_query( query_set_id, SimplifiedQueryType::Timestamp, query_index, @@ -218,9 +214,9 @@ impl QuerySet { unsafe { // If we don't have a reset state tracker which can defer resets, we must reset now. if needs_reset { - cmd_buf_raw.reset_query_pool(&self.raw, query_index..(query_index + 1)); + raw_encoder.reset_queries(&self.raw, query_index..(query_index + 1)); } - cmd_buf_raw.write_timestamp(hal::pso::PipelineStage::BOTTOM_OF_PIPE, hal_query); + raw_encoder.write_timestamp(query_set, query_index); } Ok(()) @@ -228,14 +224,14 @@ impl QuerySet { pub(super) fn validate_and_begin_pipeline_statistics_query( &self, - cmd_buf_raw: &mut B::CommandBuffer, + raw_encoder: &mut A::CommandEncoder, query_set_id: id::QuerySetId, query_index: u32, - reset_state: Option<&mut QueryResetMap>, + reset_state: Option<&mut QueryResetMap>, active_query: &mut Option<(id::QuerySetId, u32)>, ) -> Result<(), QueryUseError> { let needs_reset = reset_state.is_none(); - let hal_query = self.validate_query( + let query_set = self.validate_query( query_set_id, SimplifiedQueryType::PipelineStatistics, query_index, @@ -252,30 +248,25 @@ impl QuerySet { unsafe { // If we don't have a reset state tracker which can defer resets, we must reset now. if needs_reset { - cmd_buf_raw.reset_query_pool(&self.raw, query_index..(query_index + 1)); + raw_encoder.reset_queries(&self.raw, query_index..(query_index + 1)); } - cmd_buf_raw.begin_query(hal_query, hal::query::ControlFlags::empty()); + raw_encoder.begin_query(query_set, query_index); } Ok(()) } } -pub(super) fn end_pipeline_statistics_query( - cmd_buf_raw: &mut B::CommandBuffer, - storage: &Storage, id::QuerySetId>, +pub(super) fn end_pipeline_statistics_query( + raw_encoder: &mut A::CommandEncoder, + storage: &Storage, id::QuerySetId>, active_query: &mut Option<(id::QuerySetId, u32)>, ) -> Result<(), QueryUseError> { if let Some((query_set_id, query_index)) = active_query.take() { // We can unwrap here as the validity was validated when the active query was set let query_set = storage.get(query_set_id).unwrap(); - let hal_query = hal::query::Query:: { - pool: &query_set.raw, - id: query_index, - }; - - unsafe { cmd_buf_raw.end_query(hal_query) } + unsafe { raw_encoder.end_query(&query_set.raw, query_index) }; Ok(()) } else { @@ -284,20 +275,20 @@ pub(super) fn end_pipeline_statistics_query( } impl Global { - pub fn command_encoder_write_timestamp( + pub fn command_encoder_write_timestamp( &self, command_encoder_id: id::CommandEncoderId, query_set_id: id::QuerySetId, query_index: u32, ) -> Result<(), QueryError> { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let (query_set_guard, _) = hub.query_sets.read(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut cmd_buf_guard, command_encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let raw_encoder = cmd_buf.encoder.open(); #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -316,12 +307,12 @@ impl Global { _ => unreachable!(), })?; - query_set.validate_and_write_timestamp(cmd_buf_raw, query_set_id, query_index, None)?; + query_set.validate_and_write_timestamp(raw_encoder, query_set_id, query_index, None)?; Ok(()) } - pub fn command_encoder_resolve_query_set( + pub fn command_encoder_resolve_query_set( &self, command_encoder_id: id::CommandEncoderId, query_set_id: id::QuerySetId, @@ -330,7 +321,7 @@ impl Global { destination: id::BufferId, destination_offset: BufferAddress, ) -> Result<(), QueryError> { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); @@ -338,7 +329,7 @@ impl Global { let (buffer_guard, _) = hub.buffers.read(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut cmd_buf_guard, command_encoder_id)?; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let raw_encoder = cmd_buf.encoder.open(); #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -363,7 +354,7 @@ impl Global { let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, destination, (), BufferUse::COPY_DST) + .use_replace(&*buffer_guard, destination, (), hal::BufferUse::COPY_DST) .map_err(QueryError::InvalidBuffer)?; let dst_barrier = dst_pending.map(|pending| pending.into_hal(dst_buffer)); @@ -381,9 +372,7 @@ impl Global { .into()); } - let stride = query_set.elements * wgt::QUERY_SIZE; - let bytes_used = (stride * query_count) as BufferAddress; - + let bytes_used = (wgt::QUERY_SIZE * query_count) as BufferAddress; let buffer_start_offset = destination_offset; let buffer_end_offset = buffer_start_offset + bytes_used; @@ -391,7 +380,7 @@ impl Global { return Err(ResolveError::BufferOverrun { start_query, end_query, - stride, + stride: wgt::QUERY_SIZE, buffer_size: dst_buffer.size, buffer_start_offset, buffer_end_offset, @@ -400,18 +389,12 @@ impl Global { } unsafe { - cmd_buf_raw.pipeline_barrier( - all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - dst_barrier, - ); - cmd_buf_raw.copy_query_pool_results( + raw_encoder.transition_buffers(dst_barrier); + raw_encoder.copy_query_results( &query_set.raw, start_query..end_query, - &dst_buffer.raw.as_ref().unwrap().0, + dst_buffer.raw.as_ref().unwrap(), destination_offset, - stride, - hal::query::ResultFlags::WAIT | hal::query::ResultFlags::BITS_64, ); } diff --git a/wgpu-core/src/command/render.rs b/wgpu-core/src/command/render.rs index 974fe3ff5c..8c195fc5fd 100644 --- a/wgpu-core/src/command/render.rs +++ b/wgpu-core/src/command/render.rs @@ -10,16 +10,12 @@ use crate::{ PassErrorScope, QueryResetMap, QueryUseError, RenderCommand, RenderCommandError, StateChange, }, - conv, - device::{ - AttachmentData, AttachmentDataVec, Device, FramebufferKey, RenderPassCompatibilityError, - RenderPassContext, RenderPassKey, RenderPassLock, MAX_COLOR_TARGETS, MAX_VERTEX_BUFFERS, - }, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token}, + device::{AttachmentData, RenderPassCompatibilityError, RenderPassContext}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, pipeline::PipelineFlags, - resource::{BufferUse, Texture, TextureUse, TextureView, TextureViewInner}, + resource::{Texture, TextureView, TextureViewSource}, track::{StatefulTrackerSubset, TextureSelector, UsageConflict}, validation::{ check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError, @@ -28,7 +24,7 @@ use crate::{ }; use arrayvec::ArrayVec; -use hal::{command::CommandBuffer as _, device::Device as _}; +use hal::CommandEncoder as _; use thiserror::Error; use wgt::{ BufferAddress, BufferSize, BufferUsage, Color, IndexFormat, InputStepMode, TextureUsage, @@ -40,16 +36,7 @@ use serde::Deserialize; use serde::Serialize; use crate::track::UseExtendError; -use std::{ - borrow::{Borrow, Cow}, - collections::hash_map::Entry, - fmt, iter, - marker::PhantomData, - mem, - num::NonZeroU32, - ops::Range, - str, -}; +use std::{borrow::Cow, fmt, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, str}; /// Operation to perform to the output attachment at the start of a renderpass. #[repr(C)] @@ -93,6 +80,21 @@ pub struct PassChannel { pub read_only: bool, } +impl PassChannel { + fn hal_ops(&self) -> hal::AttachmentOp { + let mut ops = hal::AttachmentOp::empty(); + match self.load_op { + LoadOp::Load => ops |= hal::AttachmentOp::LOAD, + LoadOp::Clear => (), + }; + match self.store_op { + StoreOp::Store => ops |= hal::AttachmentOp::STORE, + StoreOp::Clear => (), + }; + ops + } +} + /// Describes a color attachment to a render pass. #[repr(C)] #[derive(Clone, Debug, PartialEq)] @@ -122,14 +124,14 @@ pub struct RenderPassDepthStencilAttachment { } impl RenderPassDepthStencilAttachment { - fn is_read_only(&self, aspects: hal::format::Aspects) -> Result { - if aspects.contains(hal::format::Aspects::DEPTH) && !self.depth.read_only { + fn is_read_only(&self, aspects: hal::FormatAspect) -> Result { + if aspects.contains(hal::FormatAspect::DEPTH) && !self.depth.read_only { return Ok(false); } if (self.depth.load_op, self.depth.store_op) != (LoadOp::Load, StoreOp::Store) { return Err(RenderPassErrorInner::InvalidDepthOps); } - if aspects.contains(hal::format::Aspects::STENCIL) && !self.stencil.read_only { + if aspects.contains(hal::FormatAspect::STENCIL) && !self.stencil.read_only { return Ok(false); } if (self.stencil.load_op, self.stencil.store_op) != (LoadOp::Load, StoreOp::Store) { @@ -153,7 +155,7 @@ pub struct RenderPassDescriptor<'a> { pub struct RenderPass { base: BasePass, parent_id: id::CommandEncoderId, - color_targets: ArrayVec<[RenderPassColorAttachment; MAX_COLOR_TARGETS]>, + color_targets: ArrayVec<[RenderPassColorAttachment; hal::MAX_COLOR_TARGETS]>, depth_stencil_target: Option, } @@ -276,7 +278,7 @@ impl VertexBufferState { #[derive(Debug, Default)] struct VertexState { - inputs: ArrayVec<[VertexBufferState; MAX_VERTEX_BUFFERS]>, + inputs: ArrayVec<[VertexBufferState; hal::MAX_VERTEX_BUFFERS]>, /// Length of the shortest vertex rate vertex buffer vertex_limit: u32, /// Buffer slot which the shortest vertex rate vertex buffer is bound to @@ -404,7 +406,7 @@ pub enum RenderPassErrorInner { mismatch: (&'static str, wgt::Extent3d), }, #[error("attachment's sample count {0} is invalid")] - InvalidSampleCount(u8), + InvalidSampleCount(u32), #[error("attachment with resolve target must be multi-sampled")] InvalidResolveSourceSampleCount, #[error("resolve target must have a sample count of 1")] @@ -418,7 +420,7 @@ pub enum RenderPassErrorInner { #[error("unable to clear non-present/read-only stencil")] InvalidStencilOps, #[error("all attachments must have the same sample count, found {actual} != {expected}")] - SampleCountMismatch { actual: u8, expected: u8 }, + SampleCountMismatch { actual: u32, expected: u32 }, #[error("setting `values_offset` to be `None` is only for internal use in render bundles")] InvalidValuesOffset, #[error("required device features not enabled: {0:?}")] @@ -499,31 +501,31 @@ fn check_device_features( struct RenderAttachment<'a> { texture_id: &'a Stored, selector: &'a TextureSelector, - previous_use: Option, - new_use: TextureUse, + previous_use: Option, + new_use: hal::TextureUse, } -struct RenderPassInfo<'a, B: hal::Backend> { +type AttachmentDataVec = ArrayVec<[T; hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1]>; + +struct RenderPassInfo<'a, A: hal::Api> { context: RenderPassContext, trackers: StatefulTrackerSubset, render_attachments: AttachmentDataVec>, used_swap_chain: Option>, is_ds_read_only: bool, extent: wgt::Extent3d, - _phantom: PhantomData, + _phantom: PhantomData, } -impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { +impl<'a, A: HalApi> RenderPassInfo<'a, A> { fn start( - raw: &mut B::CommandBuffer, + label: Option<&str>, color_attachments: &[RenderPassColorAttachment], depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>, - cmd_buf: &mut CommandBuffer, - device: &Device, - view_guard: &'a Storage, id::TextureViewId>, + cmd_buf: &mut CommandBuffer, + view_guard: &'a Storage, id::TextureViewId>, ) -> Result { profiling::scope!("start", "RenderPassInfo"); - let sample_count_limit = device.hal_limits.framebuffer_color_sample_counts; // We default to false intentionally, even if depth-stencil isn't used at all. // This allows us to use the primary raw pipeline in `RenderPipeline`, @@ -535,10 +537,9 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { let mut attachment_type_name = ""; let mut extent = None; let mut sample_count = 0; - let mut depth_stencil_aspects = hal::format::Aspects::empty(); let mut used_swap_chain = None::>; - let mut add_view = |view: &TextureView, type_name| { + let mut add_view = |view: &TextureView, type_name| { if let Some(ex) = extent { if ex != view.extent { return Err(RenderPassErrorInner::AttachmentsDimensionMismatch { @@ -561,278 +562,173 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { Ok(()) }; - let rp_key = { - let depth_stencil = match depth_stencil_attachment { - Some(at) => { - let view = cmd_buf - .trackers - .views - .use_extend(&*view_guard, at.view, (), ()) - .map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?; - add_view(view, "depth")?; - - depth_stencil_aspects = view.aspects; - if view.aspects.contains(hal::format::Aspects::COLOR) { - return Err(RenderPassErrorInner::InvalidDepthStencilAttachmentFormat( - view.format, - )); - } + let mut colors = ArrayVec::<[hal::ColorAttachment; hal::MAX_COLOR_TARGETS]>::new(); + let mut depth_stencil = None; + + if let Some(at) = depth_stencil_attachment { + let view = cmd_buf + .trackers + .views + .use_extend(&*view_guard, at.view, (), ()) + .map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?; + add_view(view, "depth")?; + + let ds_aspects = view.desc.aspects(); + if ds_aspects.contains(hal::FormatAspect::COLOR) { + return Err(RenderPassErrorInner::InvalidDepthStencilAttachmentFormat( + view.desc.format, + )); + } - let source_id = match view.inner { - TextureViewInner::Native { ref source_id, .. } => source_id, - TextureViewInner::SwapChain { .. } => { - return Err(RenderPassErrorInner::SwapChainImageAsDepthStencil); - } - }; + let source_id = match view.source { + TextureViewSource::Native(ref source_id) => source_id, + TextureViewSource::SwapChain(_) => { + return Err(RenderPassErrorInner::SwapChainImageAsDepthStencil); + } + }; + + // Using render pass for transition. + let previous_use = cmd_buf + .trackers + .textures + .query(source_id.value, view.selector.clone()); + let new_use = if at.is_read_only(ds_aspects)? { + is_ds_read_only = true; + hal::TextureUse::DEPTH_STENCIL_READ | hal::TextureUse::SAMPLED + } else { + hal::TextureUse::DEPTH_STENCIL_WRITE + }; + render_attachments.push(RenderAttachment { + texture_id: source_id, + selector: &view.selector, + previous_use, + new_use, + }); - // Using render pass for transition. + let old_use = previous_use.unwrap_or(new_use); + depth_stencil = Some(hal::DepthStencilAttachment { + target: hal::Attachment { + view: &view.raw, + usage: new_use, + boundary_usage: old_use..new_use, + }, + depth_ops: at.depth.hal_ops(), + stencil_ops: at.stencil.hal_ops(), + clear_value: (at.depth.clear_value, at.stencil.clear_value), + }); + } + + for at in color_attachments { + let color_view = cmd_buf + .trackers + .views + .use_extend(&*view_guard, at.view, (), ()) + .map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?; + add_view(color_view, "color")?; + + if !color_view.desc.aspects().contains(hal::FormatAspect::COLOR) { + return Err(RenderPassErrorInner::InvalidColorAttachmentFormat( + color_view.desc.format, + )); + } + + let boundary_usage = match color_view.source { + TextureViewSource::Native(ref source_id) => { let previous_use = cmd_buf .trackers .textures - .query(source_id.value, view.selector.clone()); - let new_use = if at.is_read_only(view.aspects)? { - is_ds_read_only = true; - TextureUse::ATTACHMENT_READ - } else { - TextureUse::ATTACHMENT_WRITE - }; + .query(source_id.value, color_view.selector.clone()); + let new_use = hal::TextureUse::COLOR_TARGET; render_attachments.push(RenderAttachment { texture_id: source_id, - selector: &view.selector, + selector: &color_view.selector, previous_use, new_use, }); - let new_layout = conv::map_texture_state(new_use, view.aspects).1; - let old_layout = match previous_use { - Some(usage) => conv::map_texture_state(usage, view.aspects).1, - None => new_layout, - }; - - let ds_at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - view.format, - device.private_features, - )), - samples: view.samples, - ops: conv::map_load_store_ops(&at.depth), - stencil_ops: conv::map_load_store_ops(&at.stencil), - layouts: old_layout..new_layout, + let old_use = previous_use.unwrap_or(new_use); + old_use..new_use + } + TextureViewSource::SwapChain(ref source_id) => { + assert!(used_swap_chain.is_none()); + used_swap_chain = Some(source_id.clone()); + + let end = hal::TextureUse::empty(); + let start = match at.channel.load_op { + LoadOp::Clear => hal::TextureUse::UNINITIALIZED, + LoadOp::Load => end, }; - Some((ds_at, new_layout)) + start..end } - None => None, }; - let mut colors = ArrayVec::new(); - let mut resolves = ArrayVec::new(); - - for at in color_attachments { - let view = cmd_buf - .trackers - .views - .use_extend(&*view_guard, at.view, (), ()) - .map_err(|_| RenderPassErrorInner::InvalidAttachment(at.view))?; - add_view(view, "color")?; - - if !view.aspects.contains(hal::format::Aspects::COLOR) { - return Err(RenderPassErrorInner::InvalidColorAttachmentFormat( - view.format, - )); - } - - let layouts = match view.inner { - TextureViewInner::Native { ref source_id, .. } => { - let previous_use = cmd_buf - .trackers - .textures - .query(source_id.value, view.selector.clone()); - let new_use = TextureUse::ATTACHMENT_WRITE; - render_attachments.push(RenderAttachment { - texture_id: source_id, - selector: &view.selector, - previous_use, - new_use, - }); - - let new_layout = - conv::map_texture_state(new_use, hal::format::Aspects::COLOR).1; - let old_layout = match previous_use { - Some(usage) => { - conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 - } - None => new_layout, - }; - old_layout..new_layout - } - TextureViewInner::SwapChain { ref source_id, .. } => { - assert!(used_swap_chain.is_none()); - used_swap_chain = Some(source_id.clone()); - - let end = hal::image::Layout::Present; - let start = match at.channel.load_op { - LoadOp::Clear => hal::image::Layout::Undefined, - LoadOp::Load => end, - }; - start..end - } - }; - - let color_at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - view.format, - device.private_features, - )), - samples: view.samples, - ops: conv::map_load_store_ops(&at.channel), - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts, - }; - colors.push((color_at, hal::image::Layout::ColorAttachmentOptimal)); - } - - for resolve_target in color_attachments.iter().flat_map(|at| at.resolve_target) { - let view = cmd_buf + let mut hal_resolve_target = None; + if let Some(resolve_target) = at.resolve_target { + let resolve_view = cmd_buf .trackers .views .use_extend(&*view_guard, resolve_target, (), ()) .map_err(|_| RenderPassErrorInner::InvalidAttachment(resolve_target))?; - if extent != Some(view.extent) { + if color_view.extent != resolve_view.extent { return Err(RenderPassErrorInner::AttachmentsDimensionMismatch { previous: (attachment_type_name, extent.unwrap_or_default()), - mismatch: ("resolve", view.extent), + mismatch: ("resolve", resolve_view.extent), }); } - if view.samples != 1 { - return Err(RenderPassErrorInner::InvalidResolveTargetSampleCount); - } - if sample_count == 1 { + if color_view.samples == 1 { return Err(RenderPassErrorInner::InvalidResolveSourceSampleCount); } + if resolve_view.samples != 1 { + return Err(RenderPassErrorInner::InvalidResolveTargetSampleCount); + } - let layouts = match view.inner { - TextureViewInner::Native { ref source_id, .. } => { + let boundary_usage = match resolve_view.source { + TextureViewSource::Native(ref source_id) => { let previous_use = cmd_buf .trackers .textures - .query(source_id.value, view.selector.clone()); - let new_use = TextureUse::ATTACHMENT_WRITE; + .query(source_id.value, resolve_view.selector.clone()); + let new_use = hal::TextureUse::COLOR_TARGET; render_attachments.push(RenderAttachment { texture_id: source_id, - selector: &view.selector, + selector: &resolve_view.selector, previous_use, new_use, }); - let new_layout = - conv::map_texture_state(new_use, hal::format::Aspects::COLOR).1; - let old_layout = match previous_use { - Some(usage) => { - conv::map_texture_state(usage, hal::format::Aspects::COLOR).1 - } - None => new_layout, - }; - old_layout..new_layout + let old_use = previous_use.unwrap_or(new_use); + old_use..new_use } - TextureViewInner::SwapChain { ref source_id, .. } => { + TextureViewSource::SwapChain(ref source_id) => { assert!(used_swap_chain.is_none()); used_swap_chain = Some(source_id.clone()); - hal::image::Layout::Undefined..hal::image::Layout::Present + hal::TextureUse::UNINITIALIZED..hal::TextureUse::empty() } }; - let resolve_at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - view.format, - device.private_features, - )), - samples: view.samples, - ops: hal::pass::AttachmentOps::new( - hal::pass::AttachmentLoadOp::DontCare, - hal::pass::AttachmentStoreOp::Store, - ), - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts, - }; - resolves.push((resolve_at, hal::image::Layout::ColorAttachmentOptimal)); + hal_resolve_target = Some(hal::Attachment { + view: &resolve_view.raw, + usage: hal::TextureUse::COLOR_TARGET, + boundary_usage, + }); } - RenderPassKey { - colors, - resolves, - depth_stencil, - } - }; + colors.push(hal::ColorAttachment { + target: hal::Attachment { + view: &color_view.raw, + usage: hal::TextureUse::COLOR_TARGET, + boundary_usage, + }, + resolve_target: hal_resolve_target, + ops: at.channel.hal_ops(), + clear_value: at.channel.clear_value, + }); + } - if sample_count & sample_count_limit == 0 { + if sample_count != 1 && sample_count != 4 { return Err(RenderPassErrorInner::InvalidSampleCount(sample_count)); } - let RenderPassLock { - ref mut render_passes, - ref mut framebuffers, - } = *device.render_passes.lock(); - let render_pass = match render_passes.entry(rp_key.clone()) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(entry) => { - let color_ids: [hal::pass::AttachmentRef; MAX_COLOR_TARGETS] = [ - (0, hal::image::Layout::ColorAttachmentOptimal), - (1, hal::image::Layout::ColorAttachmentOptimal), - (2, hal::image::Layout::ColorAttachmentOptimal), - (3, hal::image::Layout::ColorAttachmentOptimal), - ]; - - let mut resolve_ids = ArrayVec::<[_; MAX_COLOR_TARGETS]>::new(); - let mut attachment_index = color_attachments.len(); - if color_attachments - .iter() - .any(|at| at.resolve_target.is_some()) - { - for ((i, at), &(_, layout)) in color_attachments - .iter() - .enumerate() - .zip(entry.key().resolves.iter()) - { - let real_attachment_index = match at.resolve_target { - Some(_) => attachment_index + i, - None => hal::pass::ATTACHMENT_UNUSED, - }; - resolve_ids.push((real_attachment_index, layout)); - } - attachment_index += color_attachments.len(); - } - - let depth_id = depth_stencil_attachment.map(|_| { - let usage = if is_ds_read_only { - TextureUse::ATTACHMENT_READ - } else { - TextureUse::ATTACHMENT_WRITE - }; - ( - attachment_index, - conv::map_texture_state(usage, depth_stencil_aspects).1, - ) - }); - - let subpass = hal::pass::SubpassDesc { - colors: &color_ids[..color_attachments.len()], - resolves: &resolve_ids, - depth_stencil: depth_id.as_ref(), - inputs: &[], - preserves: &[], - }; - let all = entry.key().all().map(|&(ref at, _)| at.clone()); - - let pass = unsafe { - device - .raw - .create_render_pass(all, iter::once(subpass), iter::empty()) - } - .unwrap(); - entry.insert(pass) - } - }; - let view_data = AttachmentData { colors: color_attachments .iter() @@ -846,125 +742,23 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { depth_stencil: depth_stencil_attachment.map(|at| view_guard.get(at.view).unwrap()), }; let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?; - let fb_key = FramebufferKey { - attachments: view_data.map(|view| view.framebuffer_attachment.clone()), - extent, - samples: sample_count, - }; let context = RenderPassContext { - attachments: view_data.map(|view| view.format), + attachments: view_data.map(|view| view.desc.format), sample_count, }; - // Cache framebuffers by the device. - let framebuffer = match framebuffers.entry(fb_key) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let fb = unsafe { - device - .raw - .create_framebuffer( - &render_pass, - e.key().attachments.all().cloned(), - conv::map_extent(&extent, wgt::TextureDimension::D3), - ) - .or(Err(RenderPassErrorInner::OutOfMemory))? - }; - e.insert(fb) - } + let hal_desc = hal::RenderPassDescriptor { + label, + color_attachments: &colors, + depth_stencil_attachment: depth_stencil, }; - - let rect = hal::pso::Rect { - x: 0, - y: 0, - w: extent.width as _, - h: extent.height as _, - }; - let raw_views = view_data.map(|view| match view.inner { - TextureViewInner::Native { ref raw, .. } => raw, - TextureViewInner::SwapChain { ref image, .. } => Borrow::borrow(image), - }); - - //Note: the order of iteration has to match `AttachmentData::all()` - let attachments = color_attachments - .iter() - .zip(&rp_key.colors) - .zip(raw_views.colors) - .map( - |((at, &(ref rat, _layout)), image_view)| hal::command::RenderAttachmentInfo { - image_view, - clear_value: match at.channel.load_op { - LoadOp::Load => Default::default(), - LoadOp::Clear => { - use hal::format::ChannelType; - //TODO: validate sign/unsign and normalized ranges of the color values - let value = match rat.format.unwrap().base_format().1 { - ChannelType::Unorm - | ChannelType::Snorm - | ChannelType::Ufloat - | ChannelType::Sfloat - | ChannelType::Uscaled - | ChannelType::Sscaled - | ChannelType::Srgb => hal::command::ClearColor { - float32: conv::map_color_f32(&at.channel.clear_value), - }, - ChannelType::Sint => hal::command::ClearColor { - sint32: conv::map_color_i32(&at.channel.clear_value), - }, - ChannelType::Uint => hal::command::ClearColor { - uint32: conv::map_color_u32(&at.channel.clear_value), - }, - }; - hal::command::ClearValue { color: value } - } - }, - }, - ) - .chain(raw_views.resolves.into_iter().map(|image_view| { - hal::command::RenderAttachmentInfo { - image_view, - clear_value: Default::default(), - } - })) - .chain(depth_stencil_attachment.zip(raw_views.depth_stencil).map( - |(at, image_view)| hal::command::RenderAttachmentInfo { - image_view, - clear_value: match (at.depth.load_op, at.stencil.load_op) { - (LoadOp::Load, LoadOp::Load) => Default::default(), - (LoadOp::Clear, _) | (_, LoadOp::Clear) => { - let value = hal::command::ClearDepthStencil { - depth: at.depth.clear_value, - stencil: at.stencil.clear_value, - }; - hal::command::ClearValue { - depth_stencil: value, - } - } - }, - }, - )); - unsafe { - raw.begin_render_pass( - render_pass, - framebuffer, - rect, - attachments, - hal::command::SubpassContents::Inline, - ); - raw.set_scissors(0, iter::once(rect)); - raw.set_viewports( - 0, - iter::once(hal::pso::Viewport { - rect, - depth: 0.0..1.0, - }), - ); - } + cmd_buf.encoder.raw.begin_render_pass(&hal_desc); + }; Ok(Self { context, - trackers: StatefulTrackerSubset::new(B::VARIANT), + trackers: StatefulTrackerSubset::new(A::VARIANT), render_attachments, used_swap_chain, is_ds_read_only, @@ -975,14 +769,15 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { fn finish( mut self, - texture_guard: &Storage, id::TextureId>, + raw: &mut A::CommandEncoder, + texture_guard: &Storage, id::TextureId>, ) -> Result<(StatefulTrackerSubset, Option>), RenderPassErrorInner> { profiling::scope!("finish", "RenderPassInfo"); for ra in self.render_attachments { let texture = &texture_guard[ra.texture_id.value]; - check_texture_usage(texture.usage, TextureUsage::RENDER_ATTACHMENT)?; + check_texture_usage(texture.desc.usage, TextureUsage::RENDER_ATTACHMENT)?; // the tracker set of the pass is always in "extend" mode self.trackers @@ -1010,6 +805,10 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { .unwrap(); } } + + unsafe { + raw.end_render_pass(); + } Ok((self.trackers, self.used_swap_chain)) } } @@ -1017,12 +816,12 @@ impl<'a, B: GfxBackend> RenderPassInfo<'a, B> { // Common routines between render/compute impl Global { - pub fn command_encoder_run_render_pass( + pub fn command_encoder_run_render_pass( &self, encoder_id: id::CommandEncoderId, pass: &RenderPass, ) -> Result<(), RenderPassError> { - self.command_encoder_run_render_pass_impl::( + self.command_encoder_run_render_pass_impl::( encoder_id, pass.base.as_ref(), &pass.color_targets, @@ -1031,7 +830,7 @@ impl Global { } #[doc(hidden)] - pub fn command_encoder_run_render_pass_impl( + pub fn command_encoder_run_render_pass_impl( &self, encoder_id: id::CommandEncoderId, base: BasePassRef, @@ -1041,20 +840,22 @@ impl Global { profiling::scope!("run_render_pass", "CommandEncoder"); let scope = PassErrorScope::Pass(encoder_id); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); - let (cmd_buf_raw, trackers, query_reset_state) = { + let (pass_raw, trackers, query_reset_state) = { // read-only lock guard let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmb_guard, encoder_id).map_pass_err(scope)?; + // close everything while the new command encoder is filled + cmd_buf.encoder.close(); // will be reset to true if recording is done without errors cmd_buf.status = CommandEncoderStatus::Error; - cmd_buf.has_labels |= base.label.is_some(); + #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { list.push(crate::device::trace::Command::RunRenderPass { @@ -1065,13 +866,9 @@ impl Global { } let device = &device_guard[cmd_buf.device_id.value]; - let mut raw = device.cmd_allocator.extend(cmd_buf); unsafe { - if let Some(ref label) = base.label { - device.raw.set_command_buffer_name(&mut raw, label); - } - raw.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT); - } + cmd_buf.encoder.raw.begin_encoding(base.label).unwrap() //TODO: handle this better + }; let (bundle_guard, mut token) = hub.render_bundles.read(&mut token); let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); @@ -1088,15 +885,16 @@ impl Global { ); let mut info = RenderPassInfo::start( - &mut raw, + base.label, color_attachments, depth_stencil_attachment, cmd_buf, - device, &*view_guard, ) .map_pass_err(scope)?; + let raw = &mut cmd_buf.encoder.raw; + let mut state = State { pipeline_flags: PipelineFlags::empty(), binder: Binder::new(), @@ -1179,19 +977,18 @@ impl Global { if !entries.is_empty() { let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id.unwrap()].raw; - let desc_sets = entries.iter().map(|e| { - bind_group_guard[e.group_id.as_ref().unwrap().value] - .raw - .raw() - }); - let offsets = entries.iter().flat_map(|e| &e.dynamic_offsets).cloned(); - unsafe { - raw.bind_graphics_descriptor_sets( - pipeline_layout, - index as usize, - desc_sets, - offsets, - ); + for (i, e) in entries.iter().enumerate() { + let raw_bg = + &bind_group_guard[e.group_id.as_ref().unwrap().value].raw; + + unsafe { + raw.set_bind_group( + pipeline_layout, + index as u32 + i as u32, + raw_bg, + &e.dynamic_offsets, + ); + } } } } @@ -1227,15 +1024,12 @@ impl Global { .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT)); unsafe { - raw.bind_graphics_pipeline(&pipeline.raw); + raw.set_render_pipeline(&pipeline.raw); } if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) { unsafe { - raw.set_stencil_reference( - hal::pso::Face::all(), - state.stencil_reference, - ); + raw.set_stencil_reference(state.stencil_reference); } } @@ -1248,20 +1042,18 @@ impl Global { pipeline.layout_id.value, ); if !entries.is_empty() { - let desc_sets = entries.iter().map(|e| { - bind_group_guard[e.group_id.as_ref().unwrap().value] - .raw - .raw() - }); - let offsets = - entries.iter().flat_map(|e| &e.dynamic_offsets).cloned(); - unsafe { - raw.bind_graphics_descriptor_sets( - &pipeline_layout.raw, - start_index, - desc_sets, - offsets, - ); + for (i, e) in entries.iter().enumerate() { + let raw_bg = + &bind_group_guard[e.group_id.as_ref().unwrap().value].raw; + + unsafe { + raw.set_bind_group( + &pipeline_layout.raw, + start_index as u32 + i as u32, + raw_bg, + &e.dynamic_offsets, + ); + } } } @@ -1276,9 +1068,9 @@ impl Global { offset, size_bytes, |clear_offset, clear_data| unsafe { - raw.push_graphics_constants( + raw.set_push_constants( &pipeline_layout.raw, - conv::map_shader_stage_flags(range.stages), + range.stages, clear_offset, clear_data, ); @@ -1319,11 +1111,11 @@ impl Global { let buffer = info .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDEX) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDEX) .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) .map_pass_err(scope)?; check_buffer_usage(buffer.usage, BufferUsage::INDEX).map_pass_err(scope)?; - let &(ref buf_raw, _) = buffer + let buf_raw = buffer .raw .as_ref() .ok_or(RenderCommandError::DestroyedBuffer(buffer_id)) @@ -1349,13 +1141,13 @@ impl Global { }), ); - let range = hal::buffer::SubRange { + let bb = hal::BufferBinding { + buffer: buf_raw, offset, - size: Some(end - offset), + size, }; - let index_type = conv::map_index_format(index_format); unsafe { - raw.bind_index_buffer(buf_raw, range, index_type); + raw.set_index_buffer(bb, index_format); } } RenderCommand::SetVertexBuffer { @@ -1368,12 +1160,12 @@ impl Global { let buffer = info .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::VERTEX) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::VERTEX) .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) .map_pass_err(scope)?; check_buffer_usage(buffer.usage, BufferUsage::VERTEX) .map_pass_err(scope)?; - let &(ref buf_raw, _) = buffer + let buf_raw = buffer .raw .as_ref() .ok_or(RenderCommandError::DestroyedBuffer(buffer_id)) @@ -1403,19 +1195,20 @@ impl Global { }), ); - let range = hal::buffer::SubRange { + let bb = hal::BufferBinding { + buffer: buf_raw, offset, - size: size.map(|s| s.get()), + size, }; unsafe { - raw.bind_vertex_buffers(slot, iter::once((buf_raw, range))); + raw.set_vertex_buffer(slot, bb); } state.vertex.update_limits(); } RenderCommand::SetBlendConstant(ref color) => { state.blend_constant = OptionalState::Set; unsafe { - raw.set_blend_constants(conv::map_color_f32(color)); + raw.set_blend_constants(color); } } RenderCommand::SetStencilReference(value) => { @@ -1425,7 +1218,7 @@ impl Global { .contains(PipelineFlags::STENCIL_REFERENCE) { unsafe { - raw.set_stencil_reference(hal::pso::Face::all(), value); + raw.set_stencil_reference(value); } } } @@ -1435,7 +1228,6 @@ impl Global { depth_max, } => { let scope = PassErrorScope::SetViewport; - use std::{convert::TryFrom, i16}; if rect.w <= 0.0 || rect.h <= 0.0 || depth_min < 0.0 @@ -1445,20 +1237,14 @@ impl Global { { return Err(RenderCommandError::InvalidViewport).map_pass_err(scope); } - let r = hal::pso::Rect { - x: i16::try_from(rect.x.round() as i64).unwrap_or(0), - y: i16::try_from(rect.y.round() as i64).unwrap_or(0), - w: i16::try_from(rect.w.round() as i64).unwrap_or(i16::MAX), - h: i16::try_from(rect.h.round() as i64).unwrap_or(i16::MAX), + let r = hal::Rect { + x: rect.x, + y: rect.y, + w: rect.w, + h: rect.h, }; unsafe { - raw.set_viewports( - 0, - iter::once(hal::pso::Viewport { - rect: r, - depth: depth_min..depth_max, - }), - ); + raw.set_viewport(&r, depth_min..depth_max); } } RenderCommand::SetPushConstant { @@ -1491,17 +1277,11 @@ impl Global { .map_pass_err(scope)?; unsafe { - raw.push_graphics_constants( - &pipeline_layout.raw, - conv::map_shader_stage_flags(stages), - offset, - data_slice, - ) + raw.set_push_constants(&pipeline_layout.raw, stages, offset, data_slice) } } RenderCommand::SetScissor(ref rect) => { let scope = PassErrorScope::SetScissorRect; - use std::{convert::TryFrom, i16}; if rect.w == 0 || rect.h == 0 || rect.x + rect.w > info.extent.width @@ -1509,14 +1289,14 @@ impl Global { { return Err(RenderCommandError::InvalidScissorRect).map_pass_err(scope); } - let r = hal::pso::Rect { - x: i16::try_from(rect.x).unwrap_or(0), - y: i16::try_from(rect.y).unwrap_or(0), - w: i16::try_from(rect.w).unwrap_or(i16::MAX), - h: i16::try_from(rect.h).unwrap_or(i16::MAX), + let r = hal::Rect { + x: rect.x, + y: rect.y, + w: rect.w, + h: rect.h, }; unsafe { - raw.set_scissors(0, iter::once(r)); + raw.set_scissor_rect(&r); } } RenderCommand::Draw { @@ -1555,10 +1335,7 @@ impl Global { } unsafe { - raw.draw( - first_vertex..first_vertex + vertex_count, - first_instance..first_instance + instance_count, - ); + raw.draw(first_vertex, vertex_count, first_instance, instance_count); } } RenderCommand::DrawIndexed { @@ -1599,9 +1376,11 @@ impl Global { unsafe { raw.draw_indexed( - first_index..first_index + index_count, + first_index, + index_count, base_vertex, - first_instance..first_instance + instance_count, + first_instance, + instance_count, ); } } @@ -1621,7 +1400,7 @@ impl Global { let stride = match indexed { false => mem::size_of::(), true => mem::size_of::(), - } as u64; + }; if count.is_some() { check_device_features( @@ -1634,12 +1413,12 @@ impl Global { let indirect_buffer = info .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDIRECT) .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, BufferUsage::INDIRECT) .map_pass_err(scope)?; - let &(ref indirect_raw, _) = indirect_buffer + let indirect_raw = indirect_buffer .raw .as_ref() .ok_or(RenderCommandError::DestroyedBuffer(buffer_id)) @@ -1647,7 +1426,7 @@ impl Global { let actual_count = count.map_or(1, |c| c.get()); - let end_offset = offset + stride * actual_count as u64; + let end_offset = offset + stride as u64 * actual_count as u64; if end_offset > indirect_buffer.size { return Err(RenderPassErrorInner::IndirectBufferOverrun { count, @@ -1671,20 +1450,10 @@ impl Global { match indexed { false => unsafe { - raw.draw_indirect( - indirect_raw, - offset, - actual_count, - stride as u32, - ); + raw.draw_indirect(indirect_raw, offset, actual_count); }, true => unsafe { - raw.draw_indexed_indirect( - indirect_raw, - offset, - actual_count, - stride as u32, - ); + raw.draw_indexed_indirect(indirect_raw, offset, actual_count); }, } } @@ -1717,12 +1486,12 @@ impl Global { let indirect_buffer = info .trackers .buffers - .use_extend(&*buffer_guard, buffer_id, (), BufferUse::INDIRECT) + .use_extend(&*buffer_guard, buffer_id, (), hal::BufferUse::INDIRECT) .map_err(|e| RenderCommandError::Buffer(buffer_id, e)) .map_pass_err(scope)?; check_buffer_usage(indirect_buffer.usage, BufferUsage::INDIRECT) .map_pass_err(scope)?; - let &(ref indirect_raw, _) = indirect_buffer + let indirect_raw = indirect_buffer .raw .as_ref() .ok_or(RenderCommandError::DestroyedBuffer(buffer_id)) @@ -1731,12 +1500,17 @@ impl Global { let count_buffer = info .trackers .buffers - .use_extend(&*buffer_guard, count_buffer_id, (), BufferUse::INDIRECT) + .use_extend( + &*buffer_guard, + count_buffer_id, + (), + hal::BufferUse::INDIRECT, + ) .map_err(|e| RenderCommandError::Buffer(count_buffer_id, e)) .map_pass_err(scope)?; check_buffer_usage(count_buffer.usage, BufferUsage::INDIRECT) .map_pass_err(scope)?; - let &(ref count_raw, _) = count_buffer + let count_raw = count_buffer .raw .as_ref() .ok_or(RenderCommandError::DestroyedBuffer(count_buffer_id)) @@ -1792,7 +1566,6 @@ impl Global { count_raw, count_buffer_offset, max_count, - stride as u32, ); }, true => unsafe { @@ -1802,19 +1575,18 @@ impl Global { count_raw, count_buffer_offset, max_count, - stride as u32, ); }, } } - RenderCommand::PushDebugGroup { color, len } => { + RenderCommand::PushDebugGroup { color: _, len } => { state.debug_scope_depth += 1; let label = str::from_utf8(&base.string_data[string_offset..string_offset + len]) .unwrap(); string_offset += len; unsafe { - raw.begin_debug_marker(label, color); + raw.begin_debug_marker(label); } } RenderCommand::PopDebugGroup => { @@ -1828,13 +1600,13 @@ impl Global { raw.end_debug_marker(); } } - RenderCommand::InsertDebugMarker { color, len } => { + RenderCommand::InsertDebugMarker { color: _, len } => { let label = str::from_utf8(&base.string_data[string_offset..string_offset + len]) .unwrap(); string_offset += len; unsafe { - raw.insert_debug_marker(label, color); + raw.insert_debug_marker(label); } } RenderCommand::WriteTimestamp { @@ -1857,7 +1629,7 @@ impl Global { query_set .validate_and_write_timestamp( - &mut raw, + raw, query_set_id, query_index, Some(&mut query_reset_state), @@ -1884,7 +1656,7 @@ impl Global { query_set .validate_and_begin_pipeline_statistics_query( - &mut raw, + raw, query_set_id, query_index, Some(&mut query_reset_state), @@ -1895,12 +1667,8 @@ impl Global { RenderCommand::EndPipelineStatisticsQuery => { let scope = PassErrorScope::EndPipelineStatisticsQuery; - end_pipeline_statistics_query( - &mut raw, - &*query_set_guard, - &mut active_query, - ) - .map_pass_err(scope)?; + end_pipeline_statistics_query(raw, &*query_set_guard, &mut active_query) + .map_pass_err(scope)?; } RenderCommand::ExecuteBundle(bundle_id) => { let scope = PassErrorScope::ExecuteBundle; @@ -1935,7 +1703,7 @@ impl Global { unsafe { bundle.execute( - &mut raw, + raw, &*pipeline_layout_guard, &*bind_group_guard, &*pipeline_guard, @@ -1961,14 +1729,16 @@ impl Global { } log::trace!("Merging {:?} with the render pass", encoder_id); - unsafe { - raw.end_render_pass(); - } - - let (trackers, used_swapchain) = info.finish(&*texture_guard).map_pass_err(scope)?; + let (trackers, used_swapchain) = + info.finish(raw, &*texture_guard).map_pass_err(scope)?; + let raw_cmd_buf = unsafe { + raw.end_encoding() + .map_err(|_| RenderPassErrorInner::OutOfMemory) + .map_pass_err(scope)? + }; cmd_buf.status = CommandEncoderStatus::Recording; cmd_buf.used_swap_chains.extend(used_swapchain); - (raw, trackers, query_reset_state) + (raw_cmd_buf, trackers, query_reset_state) }; let (mut cmb_guard, mut token) = hub.command_buffers.write(&mut token); @@ -1978,29 +1748,28 @@ impl Global { let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmb_guard, encoder_id).map_pass_err(scope)?; - let last_cmd_buf = cmd_buf.raw.last_mut().unwrap(); - - query_reset_state - .reset_queries( - last_cmd_buf, - &query_set_guard, - cmd_buf.device_id.value.0.backend(), - ) - .map_err(RenderCommandError::InvalidQuerySet) - .map_pass_err(PassErrorScope::QueryReset)?; - - super::CommandBuffer::insert_barriers( - last_cmd_buf, - &mut cmd_buf.trackers, - &trackers.buffers, - &trackers.textures, - &*buffer_guard, - &*texture_guard, - ); - unsafe { - last_cmd_buf.finish(); + { + let transit = cmd_buf.encoder.open(); + query_reset_state + .reset_queries( + transit, + &query_set_guard, + cmd_buf.device_id.value.0.backend(), + ) + .map_err(RenderCommandError::InvalidQuerySet) + .map_pass_err(PassErrorScope::QueryReset)?; + + super::CommandBuffer::insert_barriers( + transit, + &mut cmd_buf.trackers, + &trackers.buffers, + &trackers.textures, + &*buffer_guard, + &*texture_guard, + ); } - cmd_buf.raw.push(cmd_buf_raw); + cmd_buf.encoder.close(); + cmd_buf.encoder.list.push(pass_raw); Ok(()) } diff --git a/wgpu-core/src/command/transfer.rs b/wgpu-core/src/command/transfer.rs index c63e907a91..5b13badcf4 100644 --- a/wgpu-core/src/command/transfer.rs +++ b/wgpu-core/src/command/transfer.rs @@ -7,22 +7,19 @@ use crate::device::trace::Command as TraceCommand; use crate::{ command::{CommandBuffer, CommandEncoderError}, conv, - device::{all_buffer_stages, all_image_stages}, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id::{BufferId, CommandEncoderId, TextureId}, memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, - resource::{BufferUse, Texture, TextureErrorDimension, TextureUse}, + resource::{Texture, TextureErrorDimension}, track::TextureSelector, }; -use hal::command::CommandBuffer as _; +use hal::CommandEncoder as _; use thiserror::Error; use wgt::{BufferAddress, BufferUsage, Extent3d, TextureUsage}; use std::iter; -pub(crate) const BITS_PER_BYTE: u32 = 8; - pub type ImageCopyBuffer = wgt::ImageCopyBuffer; pub type ImageCopyTexture = wgt::ImageCopyTexture; @@ -60,6 +57,13 @@ pub enum TransferError { dimension: TextureErrorDimension, side: CopySide, }, + #[error("unable to select texture aspect {aspect:?} from fromat {format:?}")] + InvalidTextureAspect { + format: wgt::TextureFormat, + aspect: wgt::TextureAspect, + }, + #[error("unable to select texture mip level {level} out of {total}")] + InvalidTextureMipLevel { level: u32, total: u32 }, #[error("buffer offset {0} is not aligned to block size or `COPY_BUFFER_ALIGNMENT`")] UnalignedBufferOffset(BufferAddress), #[error("copy size {0} does not respect `COPY_BUFFER_ALIGNMENT`")] @@ -74,8 +78,6 @@ pub enum TransferError { UnalignedCopyOriginY, #[error("bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")] UnalignedBytesPerRow, - #[error("number of rows per image is not a multiple of block height")] - UnalignedRowsPerImage, #[error("number of bytes per row needs to be specified since more than one row is copied")] UnspecifiedBytesPerRow, #[error("number of rows per image needs to be specified since more than one image is copied")] @@ -103,53 +105,42 @@ pub enum CopyError { Transfer(#[from] TransferError), } -//TODO: we currently access each texture twice for a transfer, -// once only to get the aspect flags, which is unfortunate. -pub(crate) fn texture_copy_view_to_hal( - view: &ImageCopyTexture, - size: &Extent3d, - texture_guard: &Storage, TextureId>, -) -> Result< - ( - hal::image::SubresourceLayers, - TextureSelector, - hal::image::Offset, - ), - TransferError, -> { +pub(crate) fn extract_texture_selector( + copy_texture: &ImageCopyTexture, + copy_size: &Extent3d, + texture_guard: &Storage, TextureId>, +) -> Result<(TextureSelector, hal::TextureCopyBase, wgt::TextureFormat), TransferError> { let texture = texture_guard - .get(view.texture) - .map_err(|_| TransferError::InvalidTexture(view.texture))?; - - let level = view.mip_level as hal::image::Level; - let (layer, layer_count, z) = match texture.dimension { - wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => ( - view.origin.z as hal::image::Layer, - size.depth_or_array_layers as hal::image::Layer, - 0, - ), - wgt::TextureDimension::D3 => (0, 1, view.origin.z as i32), + .get(copy_texture.texture) + .map_err(|_| TransferError::InvalidTexture(copy_texture.texture))?; + + let format = texture.desc.format; + let copy_aspect = + hal::FormatAspect::from(format) & hal::FormatAspect::from(copy_texture.aspect); + if copy_aspect.is_empty() { + return Err(TransferError::InvalidTextureAspect { + format, + aspect: copy_texture.aspect, + }); + } + + let layers = match texture.desc.dimension { + wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => { + copy_texture.origin.z..copy_texture.origin.z + copy_size.depth_or_array_layers + } + wgt::TextureDimension::D3 => 0..1, + }; + let selector = TextureSelector { + levels: copy_texture.mip_level..copy_texture.mip_level + 1, + layers, + }; + let base = hal::TextureCopyBase { + origin: copy_texture.origin, + mip_level: copy_texture.mip_level, + aspect: copy_aspect, }; - // TODO: Can't satisfy clippy here unless we modify - // `TextureSelector` to use `std::ops::RangeBounds`. - #[allow(clippy::range_plus_one)] - Ok(( - hal::image::SubresourceLayers { - aspects: texture.aspects, - level, - layers: layer..layer + layer_count, - }, - TextureSelector { - levels: level..level + 1, - layers: layer..layer + layer_count, - }, - hal::image::Offset { - x: view.origin.x as i32, - y: view.origin.y as i32, - z, - }, - )) + Ok((selector, base, format)) } /// Function copied with some modifications from webgpu standard @@ -186,14 +177,15 @@ pub(crate) fn validate_linear_texture_data( } bytes_per_block * width_in_blocks }; - let rows_per_image = if let Some(rows_per_image) = layout.rows_per_image { + let block_rows_per_image = if let Some(rows_per_image) = layout.rows_per_image { rows_per_image.get() as BufferAddress } else { if copy_depth > 1 { return Err(TransferError::UnspecifiedRowsPerImage); } - copy_height + copy_height / block_height }; + let rows_per_image = block_rows_per_image * block_height; if copy_width % block_width != 0 { return Err(TransferError::UnalignedCopyWidth); @@ -201,9 +193,6 @@ pub(crate) fn validate_linear_texture_data( if copy_height % block_height != 0 { return Err(TransferError::UnalignedCopyHeight); } - if rows_per_image % block_height != 0 { - return Err(TransferError::UnalignedRowsPerImage); - } if need_copy_aligned_rows { let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT as BufferAddress; @@ -220,8 +209,7 @@ pub(crate) fn validate_linear_texture_data( let required_bytes_in_copy = if copy_width == 0 || copy_height == 0 || copy_depth == 0 { 0 } else { - let texel_block_rows_per_image = rows_per_image / block_height; - let bytes_per_image = bytes_per_row * texel_block_rows_per_image; + let bytes_per_image = bytes_per_row * block_rows_per_image; let bytes_in_last_slice = bytes_per_row * (height_in_blocks - 1) + bytes_in_last_row; bytes_per_image * (copy_depth - 1) + bytes_in_last_slice }; @@ -247,36 +235,25 @@ pub(crate) fn validate_linear_texture_data( } /// Function copied with minor modifications from webgpu standard +/// Returns the (virtual) mip level extent. pub(crate) fn validate_texture_copy_range( texture_copy_view: &ImageCopyTexture, - texture_format: wgt::TextureFormat, - texture_dimension: hal::image::Kind, + desc: &wgt::TextureDescriptor<()>, texture_side: CopySide, copy_size: &Extent3d, -) -> Result<(), TransferError> { - let (block_width, block_height) = texture_format.describe().block_dimensions; +) -> Result { + let (block_width, block_height) = desc.format.describe().block_dimensions; let block_width = block_width as u32; let block_height = block_height as u32; - let mut extent = texture_dimension.level_extent(texture_copy_view.mip_level as u8); - - // Adjust extent for the physical size of mips - if texture_copy_view.mip_level != 0 { - extent.width = conv::align_up(extent.width, block_width); - extent.height = conv::align_up(extent.height, block_height); - } - - match texture_dimension { - hal::image::Kind::D1(..) => { - if (copy_size.height, copy_size.depth_or_array_layers) != (1, 1) { - return Err(TransferError::InvalidCopySize); - } - } - hal::image::Kind::D2(_, _, array_layers, _) => { - extent.depth = array_layers as u32; - } - hal::image::Kind::D3(..) => {} - }; + let extent_virtual = desc.mip_level_size(texture_copy_view.mip_level).ok_or( + TransferError::InvalidTextureMipLevel { + level: texture_copy_view.mip_level, + total: desc.mip_level_count, + }, + )?; + // physical size can be larger than the virtual + let extent = extent_virtual.physical_size(desc.format); let x_copy_max = texture_copy_view.origin.x + copy_size.width; if x_copy_max > extent.width { @@ -299,11 +276,11 @@ pub(crate) fn validate_texture_copy_range( }); } let z_copy_max = texture_copy_view.origin.z + copy_size.depth_or_array_layers; - if z_copy_max > extent.depth { + if z_copy_max > extent.depth_or_array_layers { return Err(TransferError::TextureOverrun { start_offset: texture_copy_view.origin.z, end_offset: z_copy_max, - texture_size: extent.depth, + texture_size: extent.depth_or_array_layers, dimension: TextureErrorDimension::Z, side: texture_side, }); @@ -321,11 +298,12 @@ pub(crate) fn validate_texture_copy_range( if copy_size.height % block_height != 0 { return Err(TransferError::UnalignedCopyHeight); } - Ok(()) + + Ok(extent_virtual) } impl Global { - pub fn command_encoder_copy_buffer_to_buffer( + pub fn command_encoder_copy_buffer_to_buffer( &self, command_encoder_id: CommandEncoderId, source: BufferId, @@ -339,7 +317,7 @@ impl Global { if source == destination { return Err(TransferError::SameSourceDestinationBuffer.into()); } - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); @@ -360,9 +338,9 @@ impl Global { let (src_buffer, src_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, source, (), BufferUse::COPY_SRC) + .use_replace(&*buffer_guard, source, (), hal::BufferUse::COPY_SRC) .map_err(TransferError::InvalidBuffer)?; - let &(ref src_raw, _) = src_buffer + let src_raw = src_buffer .raw .as_ref() .ok_or(TransferError::InvalidBuffer(source))?; @@ -377,9 +355,9 @@ impl Global { let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, destination, (), BufferUse::COPY_DST) + .use_replace(&*buffer_guard, destination, (), hal::BufferUse::COPY_DST) .map_err(TransferError::InvalidBuffer)?; - let &(ref dst_raw, _) = dst_buffer + let dst_raw = dst_buffer .raw .as_ref() .ok_or(TransferError::InvalidBuffer(destination))?; @@ -448,24 +426,20 @@ impl Global { }), ); - let region = hal::command::BufferCopy { - src: source_offset, - dst: destination_offset, - size, + let region = hal::BufferCopy { + src_offset: source_offset, + dst_offset: destination_offset, + size: wgt::BufferSize::new(size).unwrap(), }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.pipeline_barrier( - all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - src_barrier.into_iter().chain(dst_barrier), - ); - cmd_buf_raw.copy_buffer(src_raw, dst_raw, iter::once(region)); + cmd_buf_raw.transition_buffers(src_barrier.into_iter().chain(dst_barrier)); + cmd_buf_raw.copy_buffer_to_buffer(src_raw, dst_raw, iter::once(region)); } Ok(()) } - pub fn command_encoder_copy_buffer_to_texture( + pub fn command_encoder_copy_buffer_to_texture( &self, command_encoder_id: CommandEncoderId, source: &ImageCopyBuffer, @@ -474,14 +448,13 @@ impl Global { ) -> Result<(), CopyError> { profiling::scope!("copy_buffer_to_texture", "CommandEncoder"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); + let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?; let (buffer_guard, mut token) = hub.buffers.read(&mut token); let (texture_guard, _) = hub.textures.read(&mut token); - let (dst_layers, dst_selector, dst_offset) = - texture_copy_view_to_hal(destination, copy_size, &*texture_guard)?; #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -497,12 +470,15 @@ impl Global { return Ok(()); } + let (dst_range, dst_base, _) = + extract_texture_selector(destination, copy_size, &*texture_guard)?; + let (src_buffer, src_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, source.buffer, (), BufferUse::COPY_SRC) + .use_replace(&*buffer_guard, source.buffer, (), hal::BufferUse::COPY_SRC) .map_err(TransferError::InvalidBuffer)?; - let &(ref src_raw, _) = src_buffer + let src_raw = src_buffer .raw .as_ref() .ok_or(TransferError::InvalidBuffer(source.buffer))?; @@ -517,38 +493,34 @@ impl Global { .use_replace( &*texture_guard, destination.texture, - dst_selector, - TextureUse::COPY_DST, + dst_range, + hal::TextureUse::COPY_DST, ) .unwrap(); - let &(ref dst_raw, _) = dst_texture + let dst_raw = dst_texture .raw .as_ref() .ok_or(TransferError::InvalidTexture(destination.texture))?; - if !dst_texture.usage.contains(TextureUsage::COPY_DST) { + if !dst_texture.desc.usage.contains(TextureUsage::COPY_DST) { return Err( TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(), ); } let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture)); - let bytes_per_block = conv::map_texture_format(dst_texture.format, cmd_buf.private_features) - .surface_desc() - .bits as u32 - / BITS_PER_BYTE; - validate_texture_copy_range( + let format_desc = dst_texture.desc.format.describe(); + let max_image_extent = validate_texture_copy_range( destination, - dst_texture.format, - dst_texture.kind, + &dst_texture.desc, CopySide::Destination, copy_size, )?; let required_buffer_bytes_in_copy = validate_linear_texture_data( &source.layout, - dst_texture.format, + dst_texture.desc.format, src_buffer.size, CopySide::Source, - bytes_per_block as BufferAddress, + format_desc.block_size as BufferAddress, copy_size, true, )?; @@ -564,58 +536,35 @@ impl Global { }), ); - let (block_width, _) = dst_texture.format.describe().block_dimensions; - if !conv::is_valid_copy_dst_texture_format(dst_texture.format) { - return Err(TransferError::CopyToForbiddenTextureFormat(dst_texture.format).into()); + if !conv::is_valid_copy_dst_texture_format(dst_texture.desc.format) { + return Err( + TransferError::CopyToForbiddenTextureFormat(dst_texture.desc.format).into(), + ); } // WebGPU uses the physical size of the texture for copies whereas vulkan uses // the virtual size. We have passed validation, so it's safe to use the // image extent data directly. We want the provided copy size to be no larger than // the virtual size. - let max_image_extent = dst_texture.kind.level_extent(destination.mip_level as _); - let image_extent = Extent3d { - width: copy_size.width.min(max_image_extent.width), - height: copy_size.height.min(max_image_extent.height), - depth_or_array_layers: copy_size.depth_or_array_layers, - }; - - let buffer_width = if let Some(bytes_per_row) = source.layout.bytes_per_row { - (bytes_per_row.get() / bytes_per_block) * block_width as u32 - } else { - image_extent.width - }; - let buffer_height = if let Some(rows_per_image) = source.layout.rows_per_image { - rows_per_image.get() - } else { - 0 - }; - let region = hal::command::BufferImageCopy { - buffer_offset: source.layout.offset, - buffer_width, - buffer_height, - image_layers: dst_layers, - image_offset: dst_offset, - image_extent: conv::map_extent(&image_extent, dst_texture.dimension), + let region = hal::BufferTextureCopy { + buffer_layout: source.layout, + texture_base: dst_base, + size: Extent3d { + width: copy_size.width.min(max_image_extent.width), + height: copy_size.height.min(max_image_extent.height), + depth_or_array_layers: copy_size.depth_or_array_layers, + }, }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.pipeline_barrier( - all_buffer_stages() | all_image_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - src_barriers.chain(dst_barriers), - ); - cmd_buf_raw.copy_buffer_to_image( - src_raw, - dst_raw, - hal::image::Layout::TransferDstOptimal, - iter::once(region), - ); + cmd_buf_raw.transition_buffers(src_barriers); + cmd_buf_raw.transition_textures(dst_barriers); + cmd_buf_raw.copy_buffer_to_texture(src_raw, dst_raw, iter::once(region)); } Ok(()) } - pub fn command_encoder_copy_texture_to_buffer( + pub fn command_encoder_copy_texture_to_buffer( &self, command_encoder_id: CommandEncoderId, source: &ImageCopyTexture, @@ -624,14 +573,13 @@ impl Global { ) -> Result<(), CopyError> { profiling::scope!("copy_texture_to_buffer", "CommandEncoder"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); + let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?; let (buffer_guard, mut token) = hub.buffers.read(&mut token); let (texture_guard, _) = hub.textures.read(&mut token); - let (src_layers, src_selector, src_offset) = - texture_copy_view_to_hal(source, copy_size, &*texture_guard)?; #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -647,31 +595,39 @@ impl Global { return Ok(()); } + let (src_range, src_base, _) = + extract_texture_selector(source, copy_size, &*texture_guard)?; + let (src_texture, src_pending) = cmd_buf .trackers .textures .use_replace( &*texture_guard, source.texture, - src_selector, - TextureUse::COPY_SRC, + src_range, + hal::TextureUse::COPY_SRC, ) .unwrap(); - let &(ref src_raw, _) = src_texture + let src_raw = src_texture .raw .as_ref() .ok_or(TransferError::InvalidTexture(source.texture))?; - if !src_texture.usage.contains(TextureUsage::COPY_SRC) { + if !src_texture.desc.usage.contains(TextureUsage::COPY_SRC) { return Err(TransferError::MissingCopySrcUsageFlag.into()); } let src_barriers = src_pending.map(|pending| pending.into_hal(src_texture)); - let (dst_buffer, dst_barriers) = cmd_buf + let (dst_buffer, dst_pending) = cmd_buf .trackers .buffers - .use_replace(&*buffer_guard, destination.buffer, (), BufferUse::COPY_DST) + .use_replace( + &*buffer_guard, + destination.buffer, + (), + hal::BufferUse::COPY_DST, + ) .map_err(TransferError::InvalidBuffer)?; - let &(ref dst_raw, _) = dst_buffer + let dst_raw = dst_buffer .raw .as_ref() .ok_or(TransferError::InvalidBuffer(destination.buffer))?; @@ -680,32 +636,25 @@ impl Global { TransferError::MissingCopyDstUsageFlag(Some(destination.buffer), None).into(), ); } - let dst_barrier = dst_barriers.map(|pending| pending.into_hal(dst_buffer)); + let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_buffer)); - let bytes_per_block = conv::map_texture_format(src_texture.format, cmd_buf.private_features) - .surface_desc() - .bits as u32 - / BITS_PER_BYTE; - validate_texture_copy_range( - source, - src_texture.format, - src_texture.kind, - CopySide::Source, - copy_size, - )?; + let format_desc = src_texture.desc.format.describe(); + let max_image_extent = + validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?; let required_buffer_bytes_in_copy = validate_linear_texture_data( &destination.layout, - src_texture.format, + src_texture.desc.format, dst_buffer.size, CopySide::Destination, - bytes_per_block as BufferAddress, + format_desc.block_size as BufferAddress, copy_size, true, )?; - let (block_width, _) = src_texture.format.describe().block_dimensions; - if !conv::is_valid_copy_src_texture_format(src_texture.format) { - return Err(TransferError::CopyFromForbiddenTextureFormat(src_texture.format).into()); + if !conv::is_valid_copy_src_texture_format(src_texture.desc.format) { + return Err( + TransferError::CopyFromForbiddenTextureFormat(src_texture.desc.format).into(), + ); } cmd_buf.buffer_memory_init_actions.extend( @@ -726,41 +675,22 @@ impl Global { // the virtual size. We have passed validation, so it's safe to use the // image extent data directly. We want the provided copy size to be no larger than // the virtual size. - let max_image_extent = src_texture.kind.level_extent(source.mip_level as _); - let image_extent = Extent3d { - width: copy_size.width.min(max_image_extent.width), - height: copy_size.height.min(max_image_extent.height), - depth_or_array_layers: copy_size.depth_or_array_layers, + let region = hal::BufferTextureCopy { + buffer_layout: destination.layout, + texture_base: src_base, + size: Extent3d { + width: copy_size.width.min(max_image_extent.width), + height: copy_size.height.min(max_image_extent.height), + depth_or_array_layers: copy_size.depth_or_array_layers, + }, }; - - let buffer_width = if let Some(bytes_per_row) = destination.layout.bytes_per_row { - (bytes_per_row.get() / bytes_per_block) * block_width as u32 - } else { - image_extent.width - }; - let buffer_height = if let Some(rows_per_image) = destination.layout.rows_per_image { - rows_per_image.get() - } else { - 0 - }; - let region = hal::command::BufferImageCopy { - buffer_offset: destination.layout.offset, - buffer_width, - buffer_height, - image_layers: src_layers, - image_offset: src_offset, - image_extent: conv::map_extent(&image_extent, src_texture.dimension), - }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.pipeline_barrier( - all_buffer_stages() | all_image_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - src_barriers.chain(dst_barrier), - ); - cmd_buf_raw.copy_image_to_buffer( + cmd_buf_raw.transition_buffers(dst_barriers); + cmd_buf_raw.transition_textures(src_barriers); + cmd_buf_raw.copy_texture_to_buffer( src_raw, - hal::image::Layout::TransferSrcOptimal, + hal::TextureUse::COPY_SRC, dst_raw, iter::once(region), ); @@ -768,7 +698,7 @@ impl Global { Ok(()) } - pub fn command_encoder_copy_texture_to_texture( + pub fn command_encoder_copy_texture_to_texture( &self, command_encoder_id: CommandEncoderId, source: &ImageCopyTexture, @@ -777,20 +707,13 @@ impl Global { ) -> Result<(), CopyError> { profiling::scope!("copy_texture_to_texture", "CommandEncoder"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut cmd_buf_guard, mut token) = hub.command_buffers.write(&mut token); let cmd_buf = CommandBuffer::get_encoder_mut(&mut *cmd_buf_guard, command_encoder_id)?; let (_, mut token) = hub.buffers.read(&mut token); // skip token let (texture_guard, _) = hub.textures.read(&mut token); - let (src_layers, src_selector, src_offset) = - texture_copy_view_to_hal(source, copy_size, &*texture_guard)?; - let (dst_layers, dst_selector, dst_offset) = - texture_copy_view_to_hal(destination, copy_size, &*texture_guard)?; - if src_layers.aspects != dst_layers.aspects { - return Err(TransferError::MismatchedAspects.into()); - } #[cfg(feature = "trace")] if let Some(ref mut list) = cmd_buf.commands { @@ -806,21 +729,29 @@ impl Global { return Ok(()); } + let (src_range, src_base, _) = + extract_texture_selector(source, copy_size, &*texture_guard)?; + let (dst_range, dst_base, _) = + extract_texture_selector(destination, copy_size, &*texture_guard)?; + if src_base.aspect != dst_base.aspect { + return Err(TransferError::MismatchedAspects.into()); + } + let (src_texture, src_pending) = cmd_buf .trackers .textures .use_replace( &*texture_guard, source.texture, - src_selector, - TextureUse::COPY_SRC, + src_range, + hal::TextureUse::COPY_SRC, ) .unwrap(); - let &(ref src_raw, _) = src_texture + let src_raw = src_texture .raw .as_ref() .ok_or(TransferError::InvalidTexture(source.texture))?; - if !src_texture.usage.contains(TextureUsage::COPY_SRC) { + if !src_texture.desc.usage.contains(TextureUsage::COPY_SRC) { return Err(TransferError::MissingCopySrcUsageFlag.into()); } //TODO: try to avoid this the collection. It's needed because both @@ -835,32 +766,26 @@ impl Global { .use_replace( &*texture_guard, destination.texture, - dst_selector, - TextureUse::COPY_DST, + dst_range, + hal::TextureUse::COPY_DST, ) .unwrap(); - let &(ref dst_raw, _) = dst_texture + let dst_raw = dst_texture .raw .as_ref() .ok_or(TransferError::InvalidTexture(destination.texture))?; - if !dst_texture.usage.contains(TextureUsage::COPY_DST) { + if !dst_texture.desc.usage.contains(TextureUsage::COPY_DST) { return Err( TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(), ); } barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_texture))); - validate_texture_copy_range( - source, - src_texture.format, - src_texture.kind, - CopySide::Source, - copy_size, - )?; - validate_texture_copy_range( + let max_src_image_extent = + validate_texture_copy_range(source, &src_texture.desc, CopySide::Source, copy_size)?; + let max_dst_image_extent = validate_texture_copy_range( destination, - dst_texture.format, - dst_texture.kind, + &dst_texture.desc, CopySide::Destination, copy_size, )?; @@ -869,37 +794,26 @@ impl Global { // the virtual size. We have passed validation, so it's safe to use the // image extent data directly. We want the provided copy size to be no larger than // the virtual size. - let max_src_image_extent = src_texture.kind.level_extent(source.mip_level as _); - let max_dst_image_extent = dst_texture.kind.level_extent(destination.mip_level as _); - let image_extent = Extent3d { - width: copy_size - .width - .min(max_src_image_extent.width.min(max_dst_image_extent.width)), - height: copy_size - .height - .min(max_src_image_extent.height.min(max_dst_image_extent.height)), - depth_or_array_layers: copy_size.depth_or_array_layers, + let region = hal::TextureCopy { + src_base, + dst_base, + size: Extent3d { + width: copy_size + .width + .min(max_src_image_extent.width.min(max_dst_image_extent.width)), + height: copy_size + .height + .min(max_src_image_extent.height.min(max_dst_image_extent.height)), + depth_or_array_layers: copy_size.depth_or_array_layers, + }, }; - - let region = hal::command::ImageCopy { - src_subresource: src_layers, - src_offset, - dst_subresource: dst_layers, - dst_offset, - extent: conv::map_extent(&image_extent, src_texture.dimension), - }; - let cmd_buf_raw = cmd_buf.raw.last_mut().unwrap(); + let cmd_buf_raw = cmd_buf.encoder.open(); unsafe { - cmd_buf_raw.pipeline_barrier( - all_image_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - barriers.into_iter(), - ); - cmd_buf_raw.copy_image( + cmd_buf_raw.transition_textures(barriers.into_iter()); + cmd_buf_raw.copy_texture_to_texture( src_raw, - hal::image::Layout::TransferSrcOptimal, + hal::TextureUse::COPY_SRC, dst_raw, - hal::image::Layout::TransferDstOptimal, iter::once(region), ); } diff --git a/wgpu-core/src/conv.rs b/wgpu-core/src/conv.rs index 8ee4dc4045..4b9336db40 100644 --- a/wgpu-core/src/conv.rs +++ b/wgpu-core/src/conv.rs @@ -2,547 +2,7 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -use crate::{ - command::{LoadOp, PassChannel, StoreOp}, - pipeline::ColorStateError, - resource, PrivateFeatures, -}; - -use std::convert::TryInto; - -pub fn map_adapter_info( - info: hal::adapter::AdapterInfo, - backend: wgt::Backend, -) -> wgt::AdapterInfo { - use hal::adapter::DeviceType as Dt; - - wgt::AdapterInfo { - name: info.name, - vendor: info.vendor, - device: info.device, - device_type: match info.device_type { - Dt::Other => wgt::DeviceType::Other, - Dt::IntegratedGpu => wgt::DeviceType::IntegratedGpu, - Dt::DiscreteGpu => wgt::DeviceType::DiscreteGpu, - Dt::VirtualGpu => wgt::DeviceType::VirtualGpu, - Dt::Cpu => wgt::DeviceType::Cpu, - }, - backend, - } -} - -pub fn map_buffer_usage(usage: wgt::BufferUsage) -> (hal::buffer::Usage, hal::memory::Properties) { - use hal::buffer::Usage as U; - use hal::memory::Properties as P; - use wgt::BufferUsage as W; - - let mut hal_memory = P::empty(); - if usage.contains(W::MAP_READ) { - hal_memory |= P::CPU_VISIBLE | P::CPU_CACHED; - } - if usage.contains(W::MAP_WRITE) { - hal_memory |= P::CPU_VISIBLE; - } - - let mut hal_usage = U::empty(); - if usage.contains(W::COPY_SRC) { - hal_usage |= U::TRANSFER_SRC; - } - if usage.contains(W::COPY_DST) { - hal_usage |= U::TRANSFER_DST; - } - if usage.contains(W::INDEX) { - hal_usage |= U::INDEX; - } - if usage.contains(W::VERTEX) { - hal_usage |= U::VERTEX; - } - if usage.contains(W::UNIFORM) { - hal_usage |= U::UNIFORM; - } - if usage.contains(W::STORAGE) { - hal_usage |= U::STORAGE; - } - if usage.contains(W::INDIRECT) { - hal_usage |= U::INDIRECT; - } - - (hal_usage, hal_memory) -} - -pub fn map_texture_usage( - usage: wgt::TextureUsage, - aspects: hal::format::Aspects, -) -> hal::image::Usage { - use hal::image::Usage as U; - use wgt::TextureUsage as W; - - let mut value = U::empty(); - if usage.contains(W::COPY_SRC) { - value |= U::TRANSFER_SRC; - } - if usage.contains(W::COPY_DST) { - value |= U::TRANSFER_DST; - } - if usage.contains(W::SAMPLED) { - value |= U::SAMPLED; - } - if usage.contains(W::STORAGE) { - value |= U::STORAGE; - } - if usage.contains(W::RENDER_ATTACHMENT) { - if aspects.intersects(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) { - value |= U::DEPTH_STENCIL_ATTACHMENT; - } else { - value |= U::COLOR_ATTACHMENT; - } - } - // Note: TextureUsage::Present does not need to be handled explicitly - // TODO: HAL Transient Attachment, HAL Input Attachment - value -} - -pub fn map_binding_type(binding: &wgt::BindGroupLayoutEntry) -> hal::pso::DescriptorType { - use hal::pso; - use wgt::BindingType as Bt; - match binding.ty { - Bt::Buffer { - ty, - has_dynamic_offset, - min_binding_size: _, - } => pso::DescriptorType::Buffer { - ty: match ty { - wgt::BufferBindingType::Uniform => pso::BufferDescriptorType::Uniform, - wgt::BufferBindingType::Storage { read_only } => { - pso::BufferDescriptorType::Storage { read_only } - } - }, - format: pso::BufferDescriptorFormat::Structured { - dynamic_offset: has_dynamic_offset, - }, - }, - Bt::Sampler { .. } => pso::DescriptorType::Sampler, - Bt::Texture { .. } => pso::DescriptorType::Image { - ty: pso::ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, - Bt::StorageTexture { access, .. } => pso::DescriptorType::Image { - ty: pso::ImageDescriptorType::Storage { - read_only: match access { - wgt::StorageTextureAccess::ReadOnly => true, - _ => false, - }, - }, - }, - } -} - -pub fn map_shader_stage_flags(shader_stage_flags: wgt::ShaderStage) -> hal::pso::ShaderStageFlags { - use hal::pso::ShaderStageFlags as H; - use wgt::ShaderStage as Ss; - - let mut value = H::empty(); - if shader_stage_flags.contains(Ss::VERTEX) { - value |= H::VERTEX; - } - if shader_stage_flags.contains(Ss::FRAGMENT) { - value |= H::FRAGMENT; - } - if shader_stage_flags.contains(Ss::COMPUTE) { - value |= H::COMPUTE; - } - value -} - -pub fn map_hal_flags_to_shader_stage( - shader_stage_flags: hal::pso::ShaderStageFlags, -) -> wgt::ShaderStage { - use hal::pso::ShaderStageFlags as H; - use wgt::ShaderStage as Ss; - - let mut value = Ss::empty(); - if shader_stage_flags.contains(H::VERTEX) { - value |= Ss::VERTEX; - } - if shader_stage_flags.contains(H::FRAGMENT) { - value |= Ss::FRAGMENT; - } - if shader_stage_flags.contains(H::COMPUTE) { - value |= Ss::COMPUTE; - } - value -} - -pub fn map_extent(extent: &wgt::Extent3d, dim: wgt::TextureDimension) -> hal::image::Extent { - hal::image::Extent { - width: extent.width, - height: extent.height, - depth: match dim { - wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => 1, - wgt::TextureDimension::D3 => extent.depth_or_array_layers, - }, - } -} - -pub fn map_primitive_topology(primitive_topology: wgt::PrimitiveTopology) -> hal::pso::Primitive { - use hal::pso::Primitive as H; - use wgt::PrimitiveTopology as Pt; - match primitive_topology { - Pt::PointList => H::PointList, - Pt::LineList => H::LineList, - Pt::LineStrip => H::LineStrip, - Pt::TriangleList => H::TriangleList, - Pt::TriangleStrip => H::TriangleStrip, - } -} - -pub fn map_color_target_state( - desc: &wgt::ColorTargetState, -) -> Result { - let color_mask = desc.write_mask; - let blend = desc - .blend - .as_ref() - .map(|bs| { - Ok(hal::pso::BlendState { - color: map_blend_component(&bs.color)?, - alpha: map_blend_component(&bs.alpha)?, - }) - }) - .transpose()?; - Ok(hal::pso::ColorBlendDesc { - mask: map_color_write_flags(color_mask), - blend, - }) -} - -fn map_color_write_flags(flags: wgt::ColorWrite) -> hal::pso::ColorMask { - use hal::pso::ColorMask as H; - use wgt::ColorWrite as Cw; - - let mut value = H::empty(); - if flags.contains(Cw::RED) { - value |= H::RED; - } - if flags.contains(Cw::GREEN) { - value |= H::GREEN; - } - if flags.contains(Cw::BLUE) { - value |= H::BLUE; - } - if flags.contains(Cw::ALPHA) { - value |= H::ALPHA; - } - value -} - -fn map_blend_component( - component: &wgt::BlendComponent, -) -> Result { - use hal::pso::BlendOp as H; - use wgt::BlendOperation as Bo; - Ok(match *component { - wgt::BlendComponent { - operation: Bo::Add, - src_factor, - dst_factor, - } => H::Add { - src: map_blend_factor(src_factor), - dst: map_blend_factor(dst_factor), - }, - wgt::BlendComponent { - operation: Bo::Subtract, - src_factor, - dst_factor, - } => H::Sub { - src: map_blend_factor(src_factor), - dst: map_blend_factor(dst_factor), - }, - wgt::BlendComponent { - operation: Bo::ReverseSubtract, - src_factor, - dst_factor, - } => H::RevSub { - src: map_blend_factor(src_factor), - dst: map_blend_factor(dst_factor), - }, - wgt::BlendComponent { - operation: Bo::Min, - src_factor: wgt::BlendFactor::One, - dst_factor: wgt::BlendFactor::One, - } => H::Min, - wgt::BlendComponent { - operation: Bo::Max, - src_factor: wgt::BlendFactor::One, - dst_factor: wgt::BlendFactor::One, - } => H::Max, - _ => return Err(ColorStateError::InvalidMinMaxBlendFactors(*component)), - }) -} - -fn map_blend_factor(blend_factor: wgt::BlendFactor) -> hal::pso::Factor { - use hal::pso::Factor as H; - use wgt::BlendFactor as Bf; - match blend_factor { - Bf::Zero => H::Zero, - Bf::One => H::One, - Bf::Src => H::SrcColor, - Bf::OneMinusSrc => H::OneMinusSrcColor, - Bf::SrcAlpha => H::SrcAlpha, - Bf::OneMinusSrcAlpha => H::OneMinusSrcAlpha, - Bf::Dst => H::DstColor, - Bf::OneMinusDst => H::OneMinusDstColor, - Bf::DstAlpha => H::DstAlpha, - Bf::OneMinusDstAlpha => H::OneMinusDstAlpha, - Bf::SrcAlphaSaturated => H::SrcAlphaSaturate, - Bf::Constant => H::ConstColor, - Bf::OneMinusConstant => H::OneMinusConstColor, - } -} - -pub fn map_depth_stencil_state(desc: &wgt::DepthStencilState) -> hal::pso::DepthStencilDesc { - hal::pso::DepthStencilDesc { - depth: if desc.is_depth_enabled() { - Some(hal::pso::DepthTest { - fun: map_compare_function(desc.depth_compare), - write: desc.depth_write_enabled, - }) - } else { - None - }, - depth_bounds: false, // TODO - stencil: if desc.stencil.is_enabled() { - let s = &desc.stencil; - Some(hal::pso::StencilTest { - faces: hal::pso::Sided { - front: map_stencil_face(&s.front), - back: map_stencil_face(&s.back), - }, - read_masks: hal::pso::State::Static(hal::pso::Sided::new(s.read_mask)), - write_masks: hal::pso::State::Static(hal::pso::Sided::new(s.write_mask)), - reference_values: if s.needs_ref_value() { - hal::pso::State::Dynamic - } else { - hal::pso::State::Static(hal::pso::Sided::new(0)) - }, - }) - } else { - None - }, - } -} - -fn map_stencil_face(stencil_state_face_desc: &wgt::StencilFaceState) -> hal::pso::StencilFace { - hal::pso::StencilFace { - fun: map_compare_function(stencil_state_face_desc.compare), - op_fail: map_stencil_operation(stencil_state_face_desc.fail_op), - op_depth_fail: map_stencil_operation(stencil_state_face_desc.depth_fail_op), - op_pass: map_stencil_operation(stencil_state_face_desc.pass_op), - } -} - -pub fn map_compare_function(compare_function: wgt::CompareFunction) -> hal::pso::Comparison { - use hal::pso::Comparison as H; - use wgt::CompareFunction as Cf; - match compare_function { - Cf::Never => H::Never, - Cf::Less => H::Less, - Cf::Equal => H::Equal, - Cf::LessEqual => H::LessEqual, - Cf::Greater => H::Greater, - Cf::NotEqual => H::NotEqual, - Cf::GreaterEqual => H::GreaterEqual, - Cf::Always => H::Always, - } -} - -fn map_stencil_operation(stencil_operation: wgt::StencilOperation) -> hal::pso::StencilOp { - use hal::pso::StencilOp as H; - use wgt::StencilOperation as So; - match stencil_operation { - So::Keep => H::Keep, - So::Zero => H::Zero, - So::Replace => H::Replace, - So::Invert => H::Invert, - So::IncrementClamp => H::IncrementClamp, - So::DecrementClamp => H::DecrementClamp, - So::IncrementWrap => H::IncrementWrap, - So::DecrementWrap => H::DecrementWrap, - } -} - -pub(crate) fn map_texture_format( - texture_format: wgt::TextureFormat, - private_features: PrivateFeatures, -) -> hal::format::Format { - use hal::format::Format as H; - use wgt::TextureFormat as Tf; - match texture_format { - // Normal 8 bit formats - Tf::R8Unorm => H::R8Unorm, - Tf::R8Snorm => H::R8Snorm, - Tf::R8Uint => H::R8Uint, - Tf::R8Sint => H::R8Sint, - - // Normal 16 bit formats - Tf::R16Uint => H::R16Uint, - Tf::R16Sint => H::R16Sint, - Tf::R16Float => H::R16Sfloat, - Tf::Rg8Unorm => H::Rg8Unorm, - Tf::Rg8Snorm => H::Rg8Snorm, - Tf::Rg8Uint => H::Rg8Uint, - Tf::Rg8Sint => H::Rg8Sint, - - // Normal 32 bit formats - Tf::R32Uint => H::R32Uint, - Tf::R32Sint => H::R32Sint, - Tf::R32Float => H::R32Sfloat, - Tf::Rg16Uint => H::Rg16Uint, - Tf::Rg16Sint => H::Rg16Sint, - Tf::Rg16Float => H::Rg16Sfloat, - Tf::Rgba8Unorm => H::Rgba8Unorm, - Tf::Rgba8UnormSrgb => H::Rgba8Srgb, - Tf::Rgba8Snorm => H::Rgba8Snorm, - Tf::Rgba8Uint => H::Rgba8Uint, - Tf::Rgba8Sint => H::Rgba8Sint, - Tf::Bgra8Unorm => H::Bgra8Unorm, - Tf::Bgra8UnormSrgb => H::Bgra8Srgb, - - // Packed 32 bit formats - Tf::Rgb10a2Unorm => H::A2r10g10b10Unorm, - Tf::Rg11b10Float => H::B10g11r11Ufloat, - - // Normal 64 bit formats - Tf::Rg32Uint => H::Rg32Uint, - Tf::Rg32Sint => H::Rg32Sint, - Tf::Rg32Float => H::Rg32Sfloat, - Tf::Rgba16Uint => H::Rgba16Uint, - Tf::Rgba16Sint => H::Rgba16Sint, - Tf::Rgba16Float => H::Rgba16Sfloat, - - // Normal 128 bit formats - Tf::Rgba32Uint => H::Rgba32Uint, - Tf::Rgba32Sint => H::Rgba32Sint, - Tf::Rgba32Float => H::Rgba32Sfloat, - - // Depth and stencil formats - Tf::Depth32Float => H::D32Sfloat, - Tf::Depth24Plus => { - if private_features.texture_d24 { - H::X8D24Unorm - } else { - H::D32Sfloat - } - } - Tf::Depth24PlusStencil8 => { - if private_features.texture_d24_s8 { - H::D24UnormS8Uint - } else { - H::D32SfloatS8Uint - } - } - - // BCn compressed formats - Tf::Bc1RgbaUnorm => H::Bc1RgbaUnorm, - Tf::Bc1RgbaUnormSrgb => H::Bc1RgbaSrgb, - Tf::Bc2RgbaUnorm => H::Bc2Unorm, - Tf::Bc2RgbaUnormSrgb => H::Bc2Srgb, - Tf::Bc3RgbaUnorm => H::Bc3Unorm, - Tf::Bc3RgbaUnormSrgb => H::Bc3Srgb, - Tf::Bc4RUnorm => H::Bc4Unorm, - Tf::Bc4RSnorm => H::Bc4Snorm, - Tf::Bc5RgUnorm => H::Bc5Unorm, - Tf::Bc5RgSnorm => H::Bc5Snorm, - Tf::Bc6hRgbSfloat => H::Bc6hSfloat, - Tf::Bc6hRgbUfloat => H::Bc6hUfloat, - Tf::Bc7RgbaUnorm => H::Bc7Unorm, - Tf::Bc7RgbaUnormSrgb => H::Bc7Srgb, - - // ETC compressed formats - Tf::Etc2RgbUnorm => H::Etc2R8g8b8Unorm, - Tf::Etc2RgbUnormSrgb => H::Etc2R8g8b8Srgb, - Tf::Etc2RgbA1Unorm => H::Etc2R8g8b8a1Unorm, - Tf::Etc2RgbA1UnormSrgb => H::Etc2R8g8b8a1Srgb, - Tf::Etc2RgbA8Unorm => H::Etc2R8g8b8a8Unorm, - Tf::Etc2RgbA8UnormSrgb => H::Etc2R8g8b8a8Unorm, - Tf::EacRUnorm => H::EacR11Unorm, - Tf::EacRSnorm => H::EacR11Snorm, - Tf::EtcRgUnorm => H::EacR11g11Unorm, - Tf::EtcRgSnorm => H::EacR11g11Snorm, - - // ASTC compressed formats - Tf::Astc4x4RgbaUnorm => H::Astc4x4Srgb, - Tf::Astc4x4RgbaUnormSrgb => H::Astc4x4Srgb, - Tf::Astc5x4RgbaUnorm => H::Astc5x4Unorm, - Tf::Astc5x4RgbaUnormSrgb => H::Astc5x4Srgb, - Tf::Astc5x5RgbaUnorm => H::Astc5x5Unorm, - Tf::Astc5x5RgbaUnormSrgb => H::Astc5x5Srgb, - Tf::Astc6x5RgbaUnorm => H::Astc6x5Unorm, - Tf::Astc6x5RgbaUnormSrgb => H::Astc6x5Srgb, - Tf::Astc6x6RgbaUnorm => H::Astc6x6Unorm, - Tf::Astc6x6RgbaUnormSrgb => H::Astc6x6Srgb, - Tf::Astc8x5RgbaUnorm => H::Astc8x5Unorm, - Tf::Astc8x5RgbaUnormSrgb => H::Astc8x5Srgb, - Tf::Astc8x6RgbaUnorm => H::Astc8x6Unorm, - Tf::Astc8x6RgbaUnormSrgb => H::Astc8x6Srgb, - Tf::Astc10x5RgbaUnorm => H::Astc10x5Unorm, - Tf::Astc10x5RgbaUnormSrgb => H::Astc10x5Srgb, - Tf::Astc10x6RgbaUnorm => H::Astc10x6Unorm, - Tf::Astc10x6RgbaUnormSrgb => H::Astc10x6Srgb, - Tf::Astc8x8RgbaUnorm => H::Astc8x8Unorm, - Tf::Astc8x8RgbaUnormSrgb => H::Astc8x8Srgb, - Tf::Astc10x8RgbaUnorm => H::Astc10x8Unorm, - Tf::Astc10x8RgbaUnormSrgb => H::Astc10x8Srgb, - Tf::Astc10x10RgbaUnorm => H::Astc10x10Unorm, - Tf::Astc10x10RgbaUnormSrgb => H::Astc10x10Srgb, - Tf::Astc12x10RgbaUnorm => H::Astc12x10Unorm, - Tf::Astc12x10RgbaUnormSrgb => H::Astc12x10Srgb, - Tf::Astc12x12RgbaUnorm => H::Astc12x12Unorm, - Tf::Astc12x12RgbaUnormSrgb => H::Astc12x12Srgb, - } -} - -pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> hal::format::Format { - use hal::format::Format as H; - use wgt::VertexFormat as Vf; - match vertex_format { - Vf::Uint8x2 => H::Rg8Uint, - Vf::Uint8x4 => H::Rgba8Uint, - Vf::Sint8x2 => H::Rg8Sint, - Vf::Sint8x4 => H::Rgba8Sint, - Vf::Unorm8x2 => H::Rg8Unorm, - Vf::Unorm8x4 => H::Rgba8Unorm, - Vf::Snorm8x2 => H::Rg8Snorm, - Vf::Snorm8x4 => H::Rgba8Snorm, - Vf::Uint16x2 => H::Rg16Uint, - Vf::Uint16x4 => H::Rgba16Uint, - Vf::Sint16x2 => H::Rg16Sint, - Vf::Sint16x4 => H::Rgba16Sint, - Vf::Unorm16x2 => H::Rg16Unorm, - Vf::Unorm16x4 => H::Rgba16Unorm, - Vf::Snorm16x2 => H::Rg16Snorm, - Vf::Snorm16x4 => H::Rgba16Snorm, - Vf::Float16x2 => H::Rg16Sfloat, - Vf::Float16x4 => H::Rgba16Sfloat, - Vf::Float32 => H::R32Sfloat, - Vf::Float32x2 => H::Rg32Sfloat, - Vf::Float32x3 => H::Rgb32Sfloat, - Vf::Float32x4 => H::Rgba32Sfloat, - Vf::Uint32 => H::R32Uint, - Vf::Uint32x2 => H::Rg32Uint, - Vf::Uint32x3 => H::Rgb32Uint, - Vf::Uint32x4 => H::Rgba32Uint, - Vf::Sint32 => H::R32Sint, - Vf::Sint32x2 => H::Rg32Sint, - Vf::Sint32x3 => H::Rgb32Sint, - Vf::Sint32x4 => H::Rgba32Sint, - Vf::Float64 => H::R64Sfloat, - Vf::Float64x2 => H::Rg64Sfloat, - Vf::Float64x3 => H::Rgb64Sfloat, - Vf::Float64x4 => H::Rgba64Sfloat, - } -} +use crate::resource; pub fn is_power_of_two(val: u32) -> bool { val != 0 && (val & (val - 1)) == 0 @@ -564,7 +24,78 @@ pub fn is_valid_copy_dst_texture_format(format: wgt::TextureFormat) -> bool { } } -pub fn map_texture_dimension_size( +pub fn map_buffer_usage(usage: wgt::BufferUsage) -> hal::BufferUse { + let mut u = hal::BufferUse::empty(); + u.set( + hal::BufferUse::MAP_READ, + usage.contains(wgt::BufferUsage::MAP_READ), + ); + u.set( + hal::BufferUse::MAP_WRITE, + usage.contains(wgt::BufferUsage::MAP_WRITE), + ); + u.set( + hal::BufferUse::COPY_SRC, + usage.contains(wgt::BufferUsage::COPY_SRC), + ); + u.set( + hal::BufferUse::COPY_DST, + usage.contains(wgt::BufferUsage::COPY_DST), + ); + u.set( + hal::BufferUse::INDEX, + usage.contains(wgt::BufferUsage::INDEX), + ); + u.set( + hal::BufferUse::VERTEX, + usage.contains(wgt::BufferUsage::VERTEX), + ); + u.set( + hal::BufferUse::UNIFORM, + usage.contains(wgt::BufferUsage::UNIFORM), + ); + u.set( + hal::BufferUse::STORAGE_LOAD | hal::BufferUse::STORAGE_STORE, + usage.contains(wgt::BufferUsage::STORAGE), + ); + u.set( + hal::BufferUse::INDIRECT, + usage.contains(wgt::BufferUsage::INDIRECT), + ); + u +} + +pub fn map_texture_usage(usage: wgt::TextureUsage, aspect: hal::FormatAspect) -> hal::TextureUse { + let mut u = hal::TextureUse::empty(); + u.set( + hal::TextureUse::COPY_SRC, + usage.contains(wgt::TextureUsage::COPY_SRC), + ); + u.set( + hal::TextureUse::COPY_DST, + usage.contains(wgt::TextureUsage::COPY_DST), + ); + u.set( + hal::TextureUse::SAMPLED, + usage.contains(wgt::TextureUsage::SAMPLED), + ); + u.set( + hal::TextureUse::STORAGE_LOAD | hal::TextureUse::STORAGE_STORE, + usage.contains(wgt::TextureUsage::STORAGE), + ); + let is_color = aspect.contains(hal::FormatAspect::COLOR); + u.set( + hal::TextureUse::COLOR_TARGET, + usage.contains(wgt::TextureUsage::RENDER_ATTACHMENT) && is_color, + ); + u.set( + hal::TextureUse::DEPTH_STENCIL_READ | hal::TextureUse::DEPTH_STENCIL_WRITE, + usage.contains(wgt::TextureUsage::RENDER_ATTACHMENT) && !is_color, + ); + u +} + +pub fn check_texture_dimension_size( dimension: wgt::TextureDimension, wgt::Extent3d { width, @@ -573,15 +104,12 @@ pub fn map_texture_dimension_size( }: wgt::Extent3d, sample_size: u32, limits: &wgt::Limits, -) -> Result { - use hal::image::Kind as H; +) -> Result<(), resource::TextureDimensionError> { use resource::{TextureDimensionError as Tde, TextureErrorDimension as Ted}; use wgt::TextureDimension::*; - let layers = depth_or_array_layers.try_into().unwrap_or(!0); - let (kind, extent_limits, sample_limit) = match dimension { + let (extent_limits, sample_limit) = match dimension { D1 => ( - H::D1(width, layers), [ limits.max_texture_dimension_1d, 1, @@ -590,7 +118,6 @@ pub fn map_texture_dimension_size( 1, ), D2 => ( - H::D2(width, height, layers, sample_size as u8), [ limits.max_texture_dimension_2d, limits.max_texture_dimension_2d, @@ -599,7 +126,6 @@ pub fn map_texture_dimension_size( 32, ), D3 => ( - H::D3(width, height, depth_or_array_layers), [ limits.max_texture_dimension_3d, limits.max_texture_dimension_3d, @@ -625,278 +151,5 @@ pub fn map_texture_dimension_size( return Err(Tde::InvalidSampleCount(sample_size)); } - Ok(kind) -} - -pub fn map_texture_view_dimension(dimension: wgt::TextureViewDimension) -> hal::image::ViewKind { - use hal::image::ViewKind as H; - use wgt::TextureViewDimension::*; - match dimension { - D1 => H::D1, - D2 => H::D2, - D2Array => H::D2Array, - Cube => H::Cube, - CubeArray => H::CubeArray, - D3 => H::D3, - } -} - -pub(crate) fn map_buffer_state(usage: resource::BufferUse) -> hal::buffer::State { - use crate::resource::BufferUse as W; - use hal::buffer::Access as A; - - let mut access = A::empty(); - if usage.contains(W::MAP_READ) { - access |= A::HOST_READ; - } - if usage.contains(W::MAP_WRITE) { - access |= A::HOST_WRITE; - } - if usage.contains(W::COPY_SRC) { - access |= A::TRANSFER_READ; - } - if usage.contains(W::COPY_DST) { - access |= A::TRANSFER_WRITE; - } - if usage.contains(W::INDEX) { - access |= A::INDEX_BUFFER_READ; - } - if usage.contains(W::VERTEX) { - access |= A::VERTEX_BUFFER_READ; - } - if usage.contains(W::UNIFORM) { - access |= A::UNIFORM_READ | A::SHADER_READ; - } - if usage.contains(W::STORAGE_LOAD) { - access |= A::SHADER_READ; - } - if usage.contains(W::STORAGE_STORE) { - access |= A::SHADER_READ | A::SHADER_WRITE; - } - if usage.contains(W::INDIRECT) { - access |= A::INDIRECT_COMMAND_READ; - } - - access -} - -pub(crate) fn map_texture_state( - usage: resource::TextureUse, - aspects: hal::format::Aspects, -) -> hal::image::State { - use crate::resource::TextureUse as W; - use hal::image::{Access as A, Layout as L}; - - let is_color = aspects.contains(hal::format::Aspects::COLOR); - let layout = match usage { - W::UNINITIALIZED => return (A::empty(), L::Undefined), - W::COPY_SRC => L::TransferSrcOptimal, - W::COPY_DST => L::TransferDstOptimal, - W::SAMPLED if is_color => L::ShaderReadOnlyOptimal, - W::ATTACHMENT_READ | W::ATTACHMENT_WRITE if is_color => L::ColorAttachmentOptimal, - _ if is_color => L::General, - W::ATTACHMENT_WRITE => L::DepthStencilAttachmentOptimal, - _ => L::DepthStencilReadOnlyOptimal, - }; - - let mut access = A::empty(); - if usage.contains(W::COPY_SRC) { - access |= A::TRANSFER_READ; - } - if usage.contains(W::COPY_DST) { - access |= A::TRANSFER_WRITE; - } - if usage.contains(W::SAMPLED) { - access |= A::SHADER_READ; - } - if usage.contains(W::ATTACHMENT_READ) { - access |= if is_color { - A::COLOR_ATTACHMENT_READ - } else { - A::DEPTH_STENCIL_ATTACHMENT_READ - }; - } - if usage.contains(W::ATTACHMENT_WRITE) { - access |= if is_color { - A::COLOR_ATTACHMENT_WRITE - } else { - A::DEPTH_STENCIL_ATTACHMENT_WRITE - }; - } - if usage.contains(W::STORAGE_LOAD) { - access |= A::SHADER_READ; - } - if usage.contains(W::STORAGE_STORE) { - access |= A::SHADER_WRITE; - } - - (access, layout) -} - -pub fn map_query_type(ty: &wgt::QueryType) -> (hal::query::Type, u32) { - match *ty { - wgt::QueryType::PipelineStatistics(pipeline_statistics) => { - let mut ps = hal::query::PipelineStatistic::empty(); - ps.set( - hal::query::PipelineStatistic::VERTEX_SHADER_INVOCATIONS, - pipeline_statistics - .contains(wgt::PipelineStatisticsTypes::VERTEX_SHADER_INVOCATIONS), - ); - ps.set( - hal::query::PipelineStatistic::CLIPPING_INVOCATIONS, - pipeline_statistics.contains(wgt::PipelineStatisticsTypes::CLIPPER_INVOCATIONS), - ); - ps.set( - hal::query::PipelineStatistic::CLIPPING_PRIMITIVES, - pipeline_statistics.contains(wgt::PipelineStatisticsTypes::CLIPPER_PRIMITIVES_OUT), - ); - ps.set( - hal::query::PipelineStatistic::FRAGMENT_SHADER_INVOCATIONS, - pipeline_statistics - .contains(wgt::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS), - ); - ps.set( - hal::query::PipelineStatistic::COMPUTE_SHADER_INVOCATIONS, - pipeline_statistics - .contains(wgt::PipelineStatisticsTypes::COMPUTE_SHADER_INVOCATIONS), - ); - - ( - hal::query::Type::PipelineStatistics(ps), - pipeline_statistics.bits().count_ones(), - ) - } - wgt::QueryType::Timestamp => (hal::query::Type::Timestamp, 1), - } -} - -pub fn map_load_store_ops(channel: &PassChannel) -> hal::pass::AttachmentOps { - hal::pass::AttachmentOps { - load: match channel.load_op { - LoadOp::Clear => hal::pass::AttachmentLoadOp::Clear, - LoadOp::Load => hal::pass::AttachmentLoadOp::Load, - }, - store: match channel.store_op { - StoreOp::Clear => hal::pass::AttachmentStoreOp::DontCare, //TODO! - StoreOp::Store => hal::pass::AttachmentStoreOp::Store, - }, - } -} - -pub fn map_color_f32(color: &wgt::Color) -> hal::pso::ColorValue { - [ - color.r as f32, - color.g as f32, - color.b as f32, - color.a as f32, - ] -} -pub fn map_color_i32(color: &wgt::Color) -> [i32; 4] { - [ - color.r as i32, - color.g as i32, - color.b as i32, - color.a as i32, - ] -} -pub fn map_color_u32(color: &wgt::Color) -> [u32; 4] { - [ - color.r as u32, - color.g as u32, - color.b as u32, - color.a as u32, - ] -} - -pub fn map_filter(filter: wgt::FilterMode) -> hal::image::Filter { - match filter { - wgt::FilterMode::Nearest => hal::image::Filter::Nearest, - wgt::FilterMode::Linear => hal::image::Filter::Linear, - } -} - -pub fn map_wrap(address: wgt::AddressMode) -> hal::image::WrapMode { - use hal::image::WrapMode as W; - use wgt::AddressMode as Am; - match address { - Am::ClampToEdge => W::Clamp, - Am::Repeat => W::Tile, - Am::MirrorRepeat => W::Mirror, - Am::ClampToBorder => W::Border, - } -} - -pub fn map_primitive_state_to_input_assembler( - desc: &wgt::PrimitiveState, -) -> hal::pso::InputAssemblerDesc { - hal::pso::InputAssemblerDesc { - primitive: map_primitive_topology(desc.topology), - with_adjacency: false, - restart_index: desc.strip_index_format.map(map_index_format), - } -} - -pub fn map_primitive_state_to_rasterizer( - desc: &wgt::PrimitiveState, - depth_stencil: Option<&wgt::DepthStencilState>, -) -> hal::pso::Rasterizer { - use hal::pso; - let depth_bias = match depth_stencil { - Some(dsd) if dsd.bias.is_enabled() => Some(pso::State::Static(pso::DepthBias { - const_factor: dsd.bias.constant as f32, - slope_factor: dsd.bias.slope_scale, - clamp: dsd.bias.clamp, - })), - _ => None, - }; - pso::Rasterizer { - depth_clamping: desc.clamp_depth, - polygon_mode: match desc.polygon_mode { - wgt::PolygonMode::Fill => pso::PolygonMode::Fill, - wgt::PolygonMode::Line => pso::PolygonMode::Line, - wgt::PolygonMode::Point => pso::PolygonMode::Point, - }, - cull_face: match desc.cull_mode { - None => pso::Face::empty(), - Some(wgt::Face::Front) => pso::Face::FRONT, - Some(wgt::Face::Back) => pso::Face::BACK, - }, - front_face: match desc.front_face { - wgt::FrontFace::Ccw => pso::FrontFace::CounterClockwise, - wgt::FrontFace::Cw => pso::FrontFace::Clockwise, - }, - depth_bias, - conservative: desc.conservative, - line_width: pso::State::Static(1.0), - } -} - -pub fn map_multisample_state(desc: &wgt::MultisampleState) -> hal::pso::Multisampling { - hal::pso::Multisampling { - rasterization_samples: desc.count as _, - sample_shading: None, - sample_mask: desc.mask, - alpha_coverage: desc.alpha_to_coverage_enabled, - alpha_to_one: false, - } -} - -pub fn map_index_format(index_format: wgt::IndexFormat) -> hal::IndexType { - match index_format { - wgt::IndexFormat::Uint16 => hal::IndexType::U16, - wgt::IndexFormat::Uint32 => hal::IndexType::U32, - } -} - -/// Take `value` and round it up to the nearest alignment `alignment`. -/// -/// ```text -/// (0, 3) -> 0 -/// (1, 3) -> 3 -/// (2, 3) -> 3 -/// (3, 3) -> 3 -/// (4, 3) -> 6 -/// ... -pub fn align_up(value: u32, alignment: u32) -> u32 { - ((value + alignment - 1) / alignment) * alignment + Ok(()) } diff --git a/wgpu-core/src/device/alloc.rs b/wgpu-core/src/device/alloc.rs deleted file mode 100644 index 5366126a3a..0000000000 --- a/wgpu-core/src/device/alloc.rs +++ /dev/null @@ -1,293 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use super::DeviceError; -use hal::device::Device as _; -use std::{borrow::Cow, iter, ptr::NonNull}; - -#[derive(Debug)] -pub struct MemoryAllocator(gpu_alloc::GpuAllocator); -#[derive(Debug)] -pub struct MemoryBlock(gpu_alloc::MemoryBlock); -struct MemoryDevice<'a, B: hal::Backend>(&'a B::Device); - -impl MemoryAllocator { - pub fn new(mem_props: hal::adapter::MemoryProperties, limits: hal::Limits) -> Self { - let mem_config = gpu_alloc::Config { - dedicated_threshold: 32 << 20, - preferred_dedicated_threshold: 8 << 20, - transient_dedicated_threshold: 128 << 20, - linear_chunk: 128 << 20, - minimal_buddy_size: 1 << 10, - initial_buddy_dedicated_size: 8 << 20, - }; - let properties = gpu_alloc::DeviceProperties { - memory_types: Cow::Owned( - mem_props - .memory_types - .iter() - .map(|mt| gpu_alloc::MemoryType { - heap: mt.heap_index as u32, - props: gpu_alloc::MemoryPropertyFlags::from_bits_truncate( - mt.properties.bits() as u8, - ), - }) - .collect::>(), - ), - memory_heaps: Cow::Owned( - mem_props - .memory_heaps - .iter() - .map(|mh| gpu_alloc::MemoryHeap { size: mh.size }) - .collect::>(), - ), - max_memory_allocation_count: if limits.max_memory_allocation_count == 0 { - log::warn!("max_memory_allocation_count is not set by gfx-rs backend"); - !0 - } else { - limits.max_memory_allocation_count.min(!0u32 as usize) as u32 - }, - max_memory_allocation_size: !0, - non_coherent_atom_size: limits.non_coherent_atom_size as u64, - buffer_device_address: false, - }; - MemoryAllocator(gpu_alloc::GpuAllocator::new(mem_config, properties)) - } - - pub fn allocate( - &mut self, - device: &B::Device, - requirements: hal::memory::Requirements, - usage: gpu_alloc::UsageFlags, - ) -> Result, DeviceError> { - assert!(requirements.alignment.is_power_of_two()); - let request = gpu_alloc::Request { - size: requirements.size, - align_mask: requirements.alignment - 1, - memory_types: requirements.type_mask, - usage, - }; - - unsafe { self.0.alloc(&MemoryDevice::(device), request) } - .map(MemoryBlock) - .map_err(|err| match err { - gpu_alloc::AllocationError::OutOfHostMemory - | gpu_alloc::AllocationError::OutOfDeviceMemory => DeviceError::OutOfMemory, - _ => panic!("Unable to allocate memory: {:?}", err), - }) - } - - pub fn free(&mut self, device: &B::Device, block: MemoryBlock) { - unsafe { self.0.dealloc(&MemoryDevice::(device), block.0) } - } - - pub fn clear(&mut self, device: &B::Device) { - unsafe { self.0.cleanup(&MemoryDevice::(device)) } - } -} - -impl MemoryBlock { - pub fn bind_buffer( - &self, - device: &B::Device, - buffer: &mut B::Buffer, - ) -> Result<(), DeviceError> { - let mem = self.0.memory(); - unsafe { - device - .bind_buffer_memory(mem, self.0.offset(), buffer) - .map_err(DeviceError::from_bind) - } - } - - pub fn bind_image(&self, device: &B::Device, image: &mut B::Image) -> Result<(), DeviceError> { - let mem = self.0.memory(); - unsafe { - device - .bind_image_memory(mem, self.0.offset(), image) - .map_err(DeviceError::from_bind) - } - } - - pub fn is_coherent(&self) -> bool { - self.0 - .props() - .contains(gpu_alloc::MemoryPropertyFlags::HOST_COHERENT) - } - - pub fn map( - &mut self, - device: &B::Device, - inner_offset: wgt::BufferAddress, - size: wgt::BufferAddress, - ) -> Result, DeviceError> { - let offset = inner_offset; - unsafe { - self.0 - .map(&MemoryDevice::(device), offset, size as usize) - .map_err(DeviceError::from) - } - } - - pub fn unmap(&mut self, device: &B::Device) { - unsafe { self.0.unmap(&MemoryDevice::(device)) }; - } - - pub fn write_bytes( - &mut self, - device: &B::Device, - inner_offset: wgt::BufferAddress, - data: &[u8], - ) -> Result<(), DeviceError> { - profiling::scope!("write_bytes"); - let offset = inner_offset; - unsafe { - self.0 - .write_bytes(&MemoryDevice::(device), offset, data) - .map_err(DeviceError::from) - } - } - - pub fn read_bytes( - &mut self, - device: &B::Device, - inner_offset: wgt::BufferAddress, - data: &mut [u8], - ) -> Result<(), DeviceError> { - profiling::scope!("read_bytes"); - let offset = inner_offset; - unsafe { - self.0 - .read_bytes(&MemoryDevice::(device), offset, data) - .map_err(DeviceError::from) - } - } - - fn segment( - &self, - inner_offset: wgt::BufferAddress, - size: Option, - ) -> hal::memory::Segment { - hal::memory::Segment { - offset: self.0.offset() + inner_offset, - size: size.or_else(|| Some(self.0.size())), - } - } - - pub fn flush_range( - &self, - device: &B::Device, - inner_offset: wgt::BufferAddress, - size: Option, - ) -> Result<(), DeviceError> { - let segment = self.segment(inner_offset, size); - let mem = self.0.memory(); - unsafe { - device - .flush_mapped_memory_ranges(iter::once((mem, segment))) - .or(Err(DeviceError::OutOfMemory)) - } - } - - pub fn invalidate_range( - &self, - device: &B::Device, - inner_offset: wgt::BufferAddress, - size: Option, - ) -> Result<(), DeviceError> { - let segment = self.segment(inner_offset, size); - let mem = self.0.memory(); - unsafe { - device - .invalidate_mapped_memory_ranges(iter::once((mem, segment))) - .or(Err(DeviceError::OutOfMemory)) - } - } -} - -impl gpu_alloc::MemoryDevice for MemoryDevice<'_, B> { - unsafe fn allocate_memory( - &self, - size: u64, - memory_type: u32, - flags: gpu_alloc::AllocationFlags, - ) -> Result { - profiling::scope!("allocate_memory"); - - assert!(flags.is_empty()); - - self.0 - .allocate_memory(hal::MemoryTypeId(memory_type as _), size) - .map_err(|_| gpu_alloc::OutOfMemory::OutOfDeviceMemory) - } - - unsafe fn deallocate_memory(&self, memory: B::Memory) { - profiling::scope!("deallocate_memory"); - self.0.free_memory(memory); - } - - unsafe fn map_memory( - &self, - memory: &mut B::Memory, - offset: u64, - size: u64, - ) -> Result, gpu_alloc::DeviceMapError> { - profiling::scope!("map_memory"); - match self.0.map_memory( - memory, - hal::memory::Segment { - offset, - size: Some(size), - }, - ) { - Ok(ptr) => Ok(NonNull::new(ptr).expect("Pointer to memory mapping must not be null")), - Err(hal::device::MapError::OutOfMemory(_)) => { - Err(gpu_alloc::DeviceMapError::OutOfDeviceMemory) - } - Err(hal::device::MapError::MappingFailed) => Err(gpu_alloc::DeviceMapError::MapFailed), - Err(other) => panic!("Unexpected map error: {:?}", other), - } - } - - unsafe fn unmap_memory(&self, memory: &mut B::Memory) { - profiling::scope!("unmap_memory"); - self.0.unmap_memory(memory); - } - - unsafe fn invalidate_memory_ranges( - &self, - ranges: &[gpu_alloc::MappedMemoryRange<'_, B::Memory>], - ) -> Result<(), gpu_alloc::OutOfMemory> { - profiling::scope!("invalidate_memory_ranges"); - self.0 - .invalidate_mapped_memory_ranges(ranges.iter().map(|r| { - ( - r.memory, - hal::memory::Segment { - offset: r.offset, - size: Some(r.size), - }, - ) - })) - .map_err(|_| gpu_alloc::OutOfMemory::OutOfHostMemory) - } - - unsafe fn flush_memory_ranges( - &self, - ranges: &[gpu_alloc::MappedMemoryRange<'_, B::Memory>], - ) -> Result<(), gpu_alloc::OutOfMemory> { - profiling::scope!("flush_memory_ranges"); - self.0 - .flush_mapped_memory_ranges(ranges.iter().map(|r| { - ( - r.memory, - hal::memory::Segment { - offset: r.offset, - size: Some(r.size), - }, - ) - })) - .map_err(|_| gpu_alloc::OutOfMemory::OutOfHostMemory) - } -} diff --git a/wgpu-core/src/device/descriptor.rs b/wgpu-core/src/device/descriptor.rs deleted file mode 100644 index d2c4f7aaa7..0000000000 --- a/wgpu-core/src/device/descriptor.rs +++ /dev/null @@ -1,175 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -use super::DeviceError; -use arrayvec::ArrayVec; - -pub use gpu_descriptor::DescriptorTotalCount; - -pub type DescriptorSet = gpu_descriptor::DescriptorSet<::DescriptorSet>; - -#[derive(Debug)] -pub struct DescriptorAllocator( - gpu_descriptor::DescriptorAllocator, -); -struct DescriptorDevice<'a, B: hal::Backend>(&'a B::Device); - -impl DescriptorAllocator { - pub fn new() -> Self { - DescriptorAllocator(gpu_descriptor::DescriptorAllocator::new(0)) - } - - pub fn allocate( - &mut self, - device: &B::Device, - layout: &B::DescriptorSetLayout, - layout_descriptor_count: &DescriptorTotalCount, - count: u32, - ) -> Result>, DeviceError> { - unsafe { - self.0.allocate( - &DescriptorDevice::(device), - layout, - gpu_descriptor::DescriptorSetLayoutCreateFlags::empty(), - layout_descriptor_count, - count, - ) - } - .map_err(|err| { - log::warn!("Descriptor set allocation failed: {}", err); - DeviceError::OutOfMemory - }) - } - - pub fn free(&mut self, device: &B::Device, sets: impl IntoIterator>) { - unsafe { self.0.free(&DescriptorDevice::(device), sets) } - } - - pub fn cleanup(&mut self, device: &B::Device) { - unsafe { self.0.cleanup(&DescriptorDevice::(device)) } - } -} - -impl - gpu_descriptor::DescriptorDevice - for DescriptorDevice<'_, B> -{ - unsafe fn create_descriptor_pool( - &self, - descriptor_count: &DescriptorTotalCount, - max_sets: u32, - flags: gpu_descriptor::DescriptorPoolCreateFlags, - ) -> Result { - profiling::scope!("create_descriptor_pool"); - let mut ranges = ArrayVec::<[_; 7]>::new(); - - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Sampler, - count: descriptor_count.sampler as _, - }); - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Image { - ty: hal::pso::ImageDescriptorType::Sampled { - with_sampler: false, - }, - }, - count: descriptor_count.sampled_image as _, - }); - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Image { - ty: hal::pso::ImageDescriptorType::Storage { read_only: false }, - }, - count: descriptor_count.storage_image as _, - }); - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Buffer { - ty: hal::pso::BufferDescriptorType::Uniform, - format: hal::pso::BufferDescriptorFormat::Structured { - dynamic_offset: false, - }, - }, - count: descriptor_count.uniform_buffer as _, - }); - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Buffer { - ty: hal::pso::BufferDescriptorType::Storage { read_only: false }, - format: hal::pso::BufferDescriptorFormat::Structured { - dynamic_offset: false, - }, - }, - count: descriptor_count.storage_buffer as _, - }); - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Buffer { - ty: hal::pso::BufferDescriptorType::Uniform, - format: hal::pso::BufferDescriptorFormat::Structured { - dynamic_offset: true, - }, - }, - count: descriptor_count.uniform_buffer_dynamic as _, - }); - ranges.push(hal::pso::DescriptorRangeDesc { - ty: hal::pso::DescriptorType::Buffer { - ty: hal::pso::BufferDescriptorType::Storage { read_only: false }, - format: hal::pso::BufferDescriptorFormat::Structured { - dynamic_offset: true, - }, - }, - count: descriptor_count.storage_buffer_dynamic as _, - }); - ranges.retain(|rd| rd.count != 0); - - match hal::device::Device::create_descriptor_pool( - self.0, - max_sets as usize, - ranges.into_iter(), - hal::pso::DescriptorPoolCreateFlags::from_bits_truncate(flags.bits()), - ) { - Ok(pool) => Ok(pool), - Err(hal::device::OutOfMemory::Host) => { - Err(gpu_descriptor::CreatePoolError::OutOfHostMemory) - } - Err(hal::device::OutOfMemory::Device) => { - Err(gpu_descriptor::CreatePoolError::OutOfDeviceMemory) - } - } - } - - unsafe fn destroy_descriptor_pool(&self, pool: B::DescriptorPool) { - profiling::scope!("destroy_descriptor_pool"); - hal::device::Device::destroy_descriptor_pool(self.0, pool); - } - - unsafe fn alloc_descriptor_sets<'a>( - &self, - pool: &mut B::DescriptorPool, - layouts: impl ExactSizeIterator, - sets: &mut impl Extend, - ) -> Result<(), gpu_descriptor::DeviceAllocationError> { - use gpu_descriptor::DeviceAllocationError as Dae; - profiling::scope!("alloc_descriptor_sets"); - - match hal::pso::DescriptorPool::allocate(pool, layouts, sets) { - Ok(()) => Ok(()), - Err(hal::pso::AllocationError::OutOfMemory(oom)) => Err(match oom { - hal::device::OutOfMemory::Host => Dae::OutOfHostMemory, - hal::device::OutOfMemory::Device => Dae::OutOfDeviceMemory, - }), - Err(hal::pso::AllocationError::OutOfPoolMemory) => Err(Dae::OutOfPoolMemory), - Err(hal::pso::AllocationError::FragmentedPool) => Err(Dae::FragmentedPool), - Err(hal::pso::AllocationError::IncompatibleLayout) => { - panic!("Incompatible descriptor set layout") - } - } - } - - unsafe fn dealloc_descriptor_sets<'a>( - &self, - pool: &mut B::DescriptorPool, - sets: impl Iterator, - ) { - profiling::scope!("dealloc_descriptor_sets"); - hal::pso::DescriptorPool::free(pool, sets) - } -} diff --git a/wgpu-core/src/device/life.rs b/wgpu-core/src/device/life.rs index 2a7d3b1b35..1f85487160 100644 --- a/wgpu-core/src/device/life.rs +++ b/wgpu-core/src/device/life.rs @@ -6,26 +6,22 @@ use crate::device::trace; use crate::{ device::{ - alloc, - descriptor::{DescriptorAllocator, DescriptorSet}, - queue::TempResource, + queue::{EncoderInFlight, TempResource}, DeviceError, }, - hub::{GfxBackend, GlobalIdentityHandlerFactory, Hub, Token}, + hub::{GlobalIdentityHandlerFactory, HalApi, Hub, Token}, id, resource, track::TrackerSet, RefCount, Stored, SubmissionIndex, }; use copyless::VecHelper as _; -use hal::device::Device as _; +use hal::Device as _; use parking_lot::Mutex; use thiserror::Error; use std::sync::atomic::Ordering; -const CLEANUP_WAIT_MS: u64 = 5000; - /// A struct that keeps lists of resources that are no longer needed by the user. #[derive(Debug, Default)] pub(super) struct SuspectedResources { @@ -90,34 +86,32 @@ impl SuspectedResources { /// A struct that keeps lists of resources that are no longer needed. #[derive(Debug)] -struct NonReferencedResources { - buffers: Vec<(B::Buffer, alloc::MemoryBlock)>, - images: Vec<(B::Image, alloc::MemoryBlock)>, +struct NonReferencedResources { + buffers: Vec, + textures: Vec, // Note: we keep the associated ID here in order to be able to check // at any point what resources are used in a submission. - image_views: Vec<(id::Valid, B::ImageView)>, - samplers: Vec, - framebuffers: Vec, - desc_sets: Vec>, - compute_pipes: Vec, - graphics_pipes: Vec, - descriptor_set_layouts: Vec, - pipeline_layouts: Vec, - query_sets: Vec, + texture_views: Vec<(id::Valid, A::TextureView)>, + samplers: Vec, + bind_groups: Vec, + compute_pipes: Vec, + render_pipes: Vec, + bind_group_layouts: Vec, + pipeline_layouts: Vec, + query_sets: Vec, } -impl NonReferencedResources { +impl NonReferencedResources { fn new() -> Self { Self { buffers: Vec::new(), - images: Vec::new(), - image_views: Vec::new(), + textures: Vec::new(), + texture_views: Vec::new(), samplers: Vec::new(), - framebuffers: Vec::new(), - desc_sets: Vec::new(), + bind_groups: Vec::new(), compute_pipes: Vec::new(), - graphics_pipes: Vec::new(), - descriptor_set_layouts: Vec::new(), + render_pipes: Vec::new(), + bind_group_layouts: Vec::new(), pipeline_layouts: Vec::new(), query_sets: Vec::new(), } @@ -125,78 +119,56 @@ impl NonReferencedResources { fn extend(&mut self, other: Self) { self.buffers.extend(other.buffers); - self.images.extend(other.images); - self.image_views.extend(other.image_views); + self.textures.extend(other.textures); + self.texture_views.extend(other.texture_views); self.samplers.extend(other.samplers); - self.framebuffers.extend(other.framebuffers); - self.desc_sets.extend(other.desc_sets); + self.bind_groups.extend(other.bind_groups); self.compute_pipes.extend(other.compute_pipes); - self.graphics_pipes.extend(other.graphics_pipes); + self.render_pipes.extend(other.render_pipes); self.query_sets.extend(other.query_sets); - assert!(other.descriptor_set_layouts.is_empty()); + assert!(other.bind_group_layouts.is_empty()); assert!(other.pipeline_layouts.is_empty()); } - unsafe fn clean( - &mut self, - device: &B::Device, - memory_allocator_mutex: &Mutex>, - descriptor_allocator_mutex: &Mutex>, - ) { - if !self.buffers.is_empty() || !self.images.is_empty() { - let mut allocator = memory_allocator_mutex.lock(); - for (raw, memory) in self.buffers.drain(..) { - log::trace!("Buffer {:?} is destroyed with memory {:?}", raw, memory); - device.destroy_buffer(raw); - allocator.free(device, memory); - } - for (raw, memory) in self.images.drain(..) { - log::trace!("Image {:?} is destroyed with memory {:?}", raw, memory); - device.destroy_image(raw); - allocator.free(device, memory); - } + unsafe fn clean(&mut self, device: &A::Device) { + for raw in self.buffers.drain(..) { + device.destroy_buffer(raw); } - - for (_, raw) in self.image_views.drain(..) { - device.destroy_image_view(raw); + for raw in self.textures.drain(..) { + device.destroy_texture(raw); + } + for (_, raw) in self.texture_views.drain(..) { + device.destroy_texture_view(raw); } for raw in self.samplers.drain(..) { device.destroy_sampler(raw); } - for raw in self.framebuffers.drain(..) { - device.destroy_framebuffer(raw); - } - - if !self.desc_sets.is_empty() { - descriptor_allocator_mutex - .lock() - .free(device, self.desc_sets.drain(..)); + for raw in self.bind_groups.drain(..) { + device.destroy_bind_group(raw); } - for raw in self.compute_pipes.drain(..) { device.destroy_compute_pipeline(raw); } - for raw in self.graphics_pipes.drain(..) { - device.destroy_graphics_pipeline(raw); + for raw in self.render_pipes.drain(..) { + device.destroy_render_pipeline(raw); } - for raw in self.descriptor_set_layouts.drain(..) { - device.destroy_descriptor_set_layout(raw); + for raw in self.bind_group_layouts.drain(..) { + device.destroy_bind_group_layout(raw); } for raw in self.pipeline_layouts.drain(..) { device.destroy_pipeline_layout(raw); } for raw in self.query_sets.drain(..) { - device.destroy_query_pool(raw); + device.destroy_query_set(raw); } } } -#[derive(Debug)] -struct ActiveSubmission { +struct ActiveSubmission { index: SubmissionIndex, - fence: B::Fence, - last_resources: NonReferencedResources, + last_resources: NonReferencedResources, mapped: Vec>, + encoders: Vec>, } #[derive(Clone, Debug, Error)] @@ -215,8 +187,7 @@ pub enum WaitIdleError { /// and register the buffer with either a submission in flight, or straight into `ready_to_map` vector. /// 3. When `ActiveSubmission` is retired, the mapped buffers associated with it are moved to `ready_to_map` vector. /// 4. Finally, `handle_mapping` issues all the callbacks. -#[derive(Debug)] -pub(super) struct LifetimeTracker { +pub(super) struct LifetimeTracker { /// Resources that the user has requested be mapped, but are still in use. mapped: Vec>, /// Buffers can be used in a submission that is yet to be made, by the @@ -229,14 +200,14 @@ pub(super) struct LifetimeTracker { /// Resources that are not referenced any more but still used by GPU. /// Grouped by submissions associated with a fence and a submission index. /// The active submissions have to be stored in FIFO order: oldest come first. - active: Vec>, + active: Vec>, /// Resources that are neither referenced or used, just life_tracker /// actual deletion. - free_resources: NonReferencedResources, + free_resources: NonReferencedResources, ready_to_map: Vec>, } -impl LifetimeTracker { +impl LifetimeTracker { pub fn new() -> Self { Self { mapped: Vec::new(), @@ -252,15 +223,15 @@ impl LifetimeTracker { pub fn track_submission( &mut self, index: SubmissionIndex, - fence: B::Fence, new_suspects: &SuspectedResources, - temp_resources: impl Iterator, alloc::MemoryBlock)>, + temp_resources: impl Iterator>, + encoders: Vec>, ) { let mut last_resources = NonReferencedResources::new(); - for (res, memory) in temp_resources { + for res in temp_resources { match res { - TempResource::Buffer(raw) => last_resources.buffers.push((raw, memory)), - TempResource::Image(raw) => last_resources.images.push((raw, memory)), + TempResource::Buffer(raw) => last_resources.buffers.push(raw), + TempResource::Texture(raw) => last_resources.textures.push(raw), } } @@ -278,9 +249,9 @@ impl LifetimeTracker { self.active.alloc().init(ActiveSubmission { index, - fence, last_resources, mapped: Vec::new(), + encoders, }); } @@ -288,80 +259,43 @@ impl LifetimeTracker { self.mapped.push(Stored { value, ref_count }); } - fn wait_idle(&self, device: &B::Device) -> Result<(), WaitIdleError> { - if !self.active.is_empty() { - log::debug!("Waiting for IDLE..."); - let status = unsafe { - device - .wait_for_fences( - self.active.iter().map(|a| &a.fence), - hal::device::WaitFor::All, - CLEANUP_WAIT_MS * 1_000_000, - ) - .map_err(DeviceError::from)? - }; - log::debug!("...Done"); - - if !status { - // We timed out while waiting for the fences - return Err(WaitIdleError::StuckGpu); - } - } - Ok(()) - } - /// Returns the last submission index that is done. pub fn triage_submissions( &mut self, - device: &B::Device, - force_wait: bool, - ) -> Result { + last_done: SubmissionIndex, + command_allocator: &Mutex>, + ) { profiling::scope!("triage_submissions"); - if force_wait { - self.wait_idle(device)?; - } + //TODO: enable when `is_sorted_by_key` is stable //debug_assert!(self.active.is_sorted_by_key(|a| a.index)); let done_count = self .active .iter() - .position(|a| unsafe { !device.get_fence_status(&a.fence).unwrap_or(false) }) + .position(|a| a.index > last_done) .unwrap_or_else(|| self.active.len()); - let last_done = match done_count.checked_sub(1) { - Some(i) => self.active[i].index, - None => return Ok(0), - }; for a in self.active.drain(..done_count) { log::trace!("Active submission {} is done", a.index); self.free_resources.extend(a.last_resources); self.ready_to_map.extend(a.mapped); - unsafe { - device.destroy_fence(a.fence); + for encoder in a.encoders { + let raw = unsafe { encoder.land() }; + command_allocator.lock().release_encoder(raw); } } - - Ok(last_done) } - pub fn cleanup( - &mut self, - device: &B::Device, - memory_allocator_mutex: &Mutex>, - descriptor_allocator_mutex: &Mutex>, - ) { + pub fn cleanup(&mut self, device: &A::Device) { profiling::scope!("cleanup"); unsafe { - self.free_resources - .clean(device, memory_allocator_mutex, descriptor_allocator_mutex); - descriptor_allocator_mutex.lock().cleanup(device); + self.free_resources.clean(device); } } pub fn schedule_resource_destruction( &mut self, - temp_resource: TempResource, - memory: alloc::MemoryBlock, + temp_resource: TempResource, last_submit_index: SubmissionIndex, ) { let resources = self @@ -370,19 +304,19 @@ impl LifetimeTracker { .find(|a| a.index == last_submit_index) .map_or(&mut self.free_resources, |a| &mut a.last_resources); match temp_resource { - TempResource::Buffer(raw) => resources.buffers.push((raw, memory)), - TempResource::Image(raw) => resources.images.push((raw, memory)), + TempResource::Buffer(raw) => resources.buffers.push(raw), + TempResource::Texture(raw) => resources.textures.push(raw), } } } -impl LifetimeTracker { +impl LifetimeTracker { pub(super) fn triage_suspected( &mut self, - hub: &Hub, + hub: &Hub, trackers: &Mutex, #[cfg(feature = "trace")] trace: Option<&Mutex>, - token: &mut Token>, + token: &mut Token>, ) { profiling::scope!("triage_suspected"); @@ -423,7 +357,7 @@ impl LifetimeTracker { .iter_mut() .find(|a| a.index == submit_index) .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .desc_sets + .bind_groups .push(res.raw); } } @@ -442,12 +376,11 @@ impl LifetimeTracker { } if let Some(res) = hub.texture_views.unregister_locked(id.0, &mut *guard) { - let raw = match res.inner { - resource::TextureViewInner::Native { raw, source_id } => { + match res.source { + resource::TextureViewSource::Native(source_id) => { self.suspected_resources.textures.push(source_id.value); - raw } - resource::TextureViewInner::SwapChain { .. } => unreachable!(), + resource::TextureViewSource::SwapChain { .. } => {} }; let submit_index = res.life_guard.submission_index.load(Ordering::Acquire); @@ -455,8 +388,8 @@ impl LifetimeTracker { .iter_mut() .find(|a| a.index == submit_index) .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .image_views - .push((id, raw)); + .texture_views + .push((id, res.raw)); } } } @@ -479,7 +412,7 @@ impl LifetimeTracker { .iter_mut() .find(|a| a.index == submit_index) .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .images + .textures .extend(res.raw); } } @@ -524,15 +457,8 @@ impl LifetimeTracker { if let Some(res) = hub.buffers.unregister_locked(id.0, &mut *guard) { let submit_index = res.life_guard.submission_index.load(Ordering::Acquire); - if let resource::BufferMapState::Init { - stage_buffer, - stage_memory, - .. - } = res.map_state - { - self.free_resources - .buffers - .push((stage_buffer, stage_memory)); + if let resource::BufferMapState::Init { stage_buffer, .. } = res.map_state { + self.free_resources.buffers.push(stage_buffer); } self.active .iter_mut() @@ -586,7 +512,7 @@ impl LifetimeTracker { .iter_mut() .find(|a| a.index == submit_index) .map_or(&mut self.free_resources, |a| &mut a.last_resources) - .graphics_pipes + .render_pipes .push(res.raw); } } @@ -632,7 +558,7 @@ impl LifetimeTracker { t.lock().add(trace::Action::DestroyBindGroupLayout(id.0)); } if let Some(lay) = hub.bind_group_layouts.unregister_locked(id.0, &mut *guard) { - self.free_resources.descriptor_set_layouts.push(lay.raw); + self.free_resources.bind_group_layouts.push(lay.raw); } } } @@ -662,8 +588,8 @@ impl LifetimeTracker { pub(super) fn triage_mapped( &mut self, - hub: &Hub, - token: &mut Token>, + hub: &Hub, + token: &mut Token>, ) { if self.mapped.is_empty() { return; @@ -692,10 +618,10 @@ impl LifetimeTracker { pub(super) fn handle_mapping( &mut self, - hub: &Hub, - raw: &B::Device, + hub: &Hub, + raw: &A::Device, trackers: &Mutex, - token: &mut Token>, + token: &mut Token>, ) -> Vec { if self.ready_to_map.is_empty() { return Vec::new(); @@ -740,10 +666,7 @@ impl LifetimeTracker { Ok(ptr) => { buffer.map_state = resource::BufferMapState::Active { ptr, - sub_range: hal::buffer::SubRange { - offset: mapping.range.start, - size: Some(size), - }, + range: mapping.range.start..mapping.range.start + size, host, }; resource::BufferMapAsyncStatus::Success diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 5a0bda3c6a..35a2119fb2 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -5,80 +5,37 @@ use crate::{ binding_model, command, conv, device::life::WaitIdleError, - hub::{ - GfxBackend, Global, GlobalIdentityHandlerFactory, Hub, Input, InvalidId, Storage, Token, - }, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Hub, Input, InvalidId, Storage, Token}, id, instance, memory_init_tracker::{MemoryInitKind, MemoryInitTracker, MemoryInitTrackerAction}, pipeline, resource, swap_chain, track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict}, validation::{self, check_buffer_usage, check_texture_usage}, - FastHashMap, Label, LabelHelpers, LifeGuard, MultiRefCount, PrivateFeatures, Stored, - SubmissionIndex, MAX_BIND_GROUPS, + FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, Stored, SubmissionIndex, }; use arrayvec::ArrayVec; use copyless::VecHelper as _; -use hal::{ - command::CommandBuffer as _, - device::Device as _, - window::{PresentationSurface as _, Surface as _}, -}; +use hal::{CommandEncoder as _, Device as _}; use parking_lot::{Mutex, MutexGuard}; use thiserror::Error; -use wgt::{BufferAddress, InputStepMode, TextureDimension, TextureFormat, TextureViewDimension}; - -use std::{ - borrow::Cow, - collections::{hash_map::Entry, BTreeMap}, - iter, - marker::PhantomData, - mem, - ops::Range, - ptr, - sync::atomic::Ordering, -}; +use wgt::{BufferAddress, TextureFormat, TextureViewDimension}; + +use std::{borrow::Cow, iter, marker::PhantomData, mem, ops::Range, ptr, sync::atomic::Ordering}; -pub mod alloc; -pub mod descriptor; mod life; pub mod queue; #[cfg(any(feature = "trace", feature = "replay"))] pub mod trace; -use smallvec::SmallVec; - -pub const MAX_COLOR_TARGETS: usize = 4; -pub const MAX_MIP_LEVELS: u32 = 16; -pub const MAX_VERTEX_BUFFERS: usize = 16; -pub const MAX_ANISOTROPY: u8 = 16; pub const SHADER_STAGE_COUNT: usize = 3; +const CLEANUP_WAIT_MS: u32 = 5000; const IMPLICIT_FAILURE: &str = "failed implicit"; +const EP_FAILURE: &str = "EP is invalid"; pub type DeviceDescriptor<'a> = wgt::DeviceDescriptor>; -pub fn all_buffer_stages() -> hal::pso::PipelineStage { - use hal::pso::PipelineStage as Ps; - Ps::DRAW_INDIRECT - | Ps::VERTEX_INPUT - | Ps::VERTEX_SHADER - | Ps::FRAGMENT_SHADER - | Ps::COMPUTE_SHADER - | Ps::TRANSFER - | Ps::HOST -} -pub fn all_image_stages() -> hal::pso::PipelineStage { - use hal::pso::PipelineStage as Ps; - Ps::EARLY_FRAGMENT_TESTS - | Ps::LATE_FRAGMENT_TESTS - | Ps::COLOR_ATTACHMENT_OUTPUT - | Ps::VERTEX_SHADER - | Ps::FRAGMENT_SHADER - | Ps::COMPUTE_SHADER - | Ps::TRANSFER -} - #[repr(C)] #[derive(Clone, Copy, Debug, PartialEq)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] @@ -91,19 +48,12 @@ pub enum HostMap { #[derive(Clone, Debug, Hash, PartialEq)] #[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))] pub(crate) struct AttachmentData { - pub colors: ArrayVec<[T; MAX_COLOR_TARGETS]>, - pub resolves: ArrayVec<[T; MAX_COLOR_TARGETS]>, + pub colors: ArrayVec<[T; hal::MAX_COLOR_TARGETS]>, + pub resolves: ArrayVec<[T; hal::MAX_COLOR_TARGETS]>, pub depth_stencil: Option, } impl Eq for AttachmentData {} impl AttachmentData { - pub(crate) fn all(&self) -> impl Iterator { - self.colors - .iter() - .chain(&self.resolves) - .chain(&self.depth_stencil) - } - pub(crate) fn map U>(&self, fun: F) -> AttachmentData { AttachmentData { colors: self.colors.iter().map(&fun).collect(), @@ -113,32 +63,23 @@ impl AttachmentData { } } -pub(crate) type AttachmentDataVec = ArrayVec<[T; MAX_COLOR_TARGETS + MAX_COLOR_TARGETS + 1]>; -pub(crate) type RenderPassKey = AttachmentData<(hal::pass::Attachment, hal::image::Layout)>; -#[derive(Debug, Eq, Hash, PartialEq)] -pub(crate) struct FramebufferKey { - pub(crate) attachments: AttachmentData, - pub(crate) extent: wgt::Extent3d, - pub(crate) samples: hal::image::NumSamples, -} - #[derive(Clone, Debug, Hash, PartialEq)] #[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))] pub(crate) struct RenderPassContext { pub attachments: AttachmentData, - pub sample_count: u8, + pub sample_count: u32, } #[derive(Clone, Debug, Error)] pub enum RenderPassCompatibilityError { #[error("Incompatible color attachment: {0:?} != {1:?}")] IncompatibleColorAttachment( - ArrayVec<[TextureFormat; MAX_COLOR_TARGETS]>, - ArrayVec<[TextureFormat; MAX_COLOR_TARGETS]>, + ArrayVec<[TextureFormat; hal::MAX_COLOR_TARGETS]>, + ArrayVec<[TextureFormat; hal::MAX_COLOR_TARGETS]>, ), #[error("Incompatible depth-stencil attachment: {0:?} != {1:?}")] IncompatibleDepthStencilAttachment(Option, Option), #[error("Incompatible sample count: {0:?} != {1:?}")] - IncompatibleSampleCount(u8, u8), + IncompatibleSampleCount(u32, u32), } impl RenderPassContext { @@ -173,28 +114,27 @@ impl RenderPassContext { type BufferMapPendingCallback = (resource::BufferMapOperation, resource::BufferMapAsyncStatus); -fn map_buffer( - raw: &B::Device, - buffer: &mut resource::Buffer, - offset: hal::buffer::Offset, +fn map_buffer( + raw: &A::Device, + buffer: &mut resource::Buffer, + offset: BufferAddress, size: BufferAddress, kind: HostMap, ) -> Result, resource::BufferAccessError> { - let &mut (_, ref mut block) = buffer - .raw - .as_mut() - .ok_or(resource::BufferAccessError::Destroyed)?; - let ptr = block.map(raw, offset, size).map_err(DeviceError::from)?; + let mapping = unsafe { + raw.map_buffer(buffer.raw.as_ref().unwrap(), offset..offset + size) + .map_err(DeviceError::from)? + }; buffer.sync_mapped_writes = match kind { - HostMap::Read if !block.is_coherent() => { - block.invalidate_range(raw, offset, Some(size))?; + HostMap::Read if !mapping.is_coherent => unsafe { + raw.invalidate_mapped_ranges( + buffer.raw.as_ref().unwrap(), + iter::once(offset..offset + size), + ); None - } - HostMap::Write if !block.is_coherent() => Some(hal::memory::Segment { - offset, - size: Some(size), - }), + }, + HostMap::Write if !mapping.is_coherent => Some(offset..offset + size), _ => None, }; @@ -206,37 +146,30 @@ fn map_buffer( // we instead just initialize the memory here and make sure it is GPU visible, so this happens at max only once for every buffer region. // // If this is a write mapping zeroing out the memory here is the only reasonable way as all data is pushed to GPU anyways. - let zero_init_needs_flush_now = !block.is_coherent() && buffer.sync_mapped_writes.is_none(); // No need to flush if it is flushed later anyways. + let zero_init_needs_flush_now = mapping.is_coherent && buffer.sync_mapped_writes.is_none(); // No need to flush if it is flushed later anyways. for uninitialized_range in buffer.initialization_status.drain(offset..(size + offset)) { let num_bytes = uninitialized_range.end - uninitialized_range.start; unsafe { ptr::write_bytes( - ptr.as_ptr().offset(uninitialized_range.start as isize), + mapping + .ptr + .as_ptr() + .offset(uninitialized_range.start as isize), 0, num_bytes as usize, ) }; if zero_init_needs_flush_now { - block.flush_range(raw, uninitialized_range.start, Some(num_bytes))?; + unsafe { + raw.flush_mapped_ranges( + buffer.raw.as_ref().unwrap(), + iter::once(uninitialized_range.start..uninitialized_range.start + num_bytes), + ) + }; } } - Ok(ptr) -} - -fn unmap_buffer( - raw: &B::Device, - buffer: &mut resource::Buffer, -) -> Result<(), resource::BufferAccessError> { - let &mut (_, ref mut block) = buffer - .raw - .as_mut() - .ok_or(resource::BufferAccessError::Destroyed)?; - if let Some(segment) = buffer.sync_mapped_writes.take() { - block.flush_range(raw, segment.offset, segment.size)?; - } - block.unmap(raw); - Ok(()) + Ok(mapping.ptr) } //Note: this logic is specifically moved out of `handle_mapping()` in order to @@ -247,10 +180,37 @@ fn fire_map_callbacks>(callback } } -#[derive(Debug)] -pub(crate) struct RenderPassLock { - pub(crate) render_passes: FastHashMap, - pub(crate) framebuffers: FastHashMap, +struct CommandAllocator { + free_encoders: Vec, +} + +impl CommandAllocator { + fn acquire_encoder( + &mut self, + device: &A::Device, + queue: &A::Queue, + ) -> Result { + match self.free_encoders.pop() { + Some(encoder) => Ok(encoder), + None => unsafe { + let hal_desc = hal::CommandEncoderDescriptor { label: None, queue }; + device.create_command_encoder(&hal_desc) + }, + } + } + + fn release_encoder(&mut self, encoder: A::CommandEncoder) { + self.free_encoders.push(encoder); + } + + fn dispose(self, device: &A::Device) { + log::info!("Destroying {} command encoders", self.free_encoders.len()); + for cmd_encoder in self.free_encoders { + unsafe { + device.destroy_command_encoder(cmd_encoder); + } + } + } } /// Structure describing a logical device. Some members are internally mutable, @@ -263,32 +223,30 @@ pub(crate) struct RenderPassLock { /// 1. `life_tracker` is locked after `hub.devices`, enforced by the type system /// 1. `self.trackers` is locked last (unenforced) /// 1. `self.trace` is locked last (unenforced) -#[derive(Debug)] -pub struct Device { - pub(crate) raw: B::Device, +pub struct Device { + pub(crate) raw: A::Device, pub(crate) adapter_id: Stored, - pub(crate) queue_group: hal::queue::QueueGroup, - pub(crate) cmd_allocator: command::CommandAllocator, - mem_allocator: Mutex>, - desc_allocator: Mutex>, + pub(crate) queue: A::Queue, + //pub(crate) cmd_allocator: command::CommandAllocator, + //mem_allocator: Mutex>, + //desc_allocator: Mutex>, //Note: The submission index here corresponds to the last submission that is done. pub(crate) life_guard: LifeGuard, + command_allocator: Mutex>, pub(crate) active_submission_index: SubmissionIndex, + fence: A::Fence, /// Has to be locked temporarily only (locked last) pub(crate) trackers: Mutex, - pub(crate) render_passes: Mutex>, // Life tracker should be locked right after the device and before anything else. - life_tracker: Mutex>, + life_tracker: Mutex>, temp_suspected: life::SuspectedResources, - pub(crate) hal_limits: hal::Limits, - pub(crate) private_features: PrivateFeatures, + pub(crate) alignments: hal::Alignments, pub(crate) limits: wgt::Limits, pub(crate) features: wgt::Features, - pub(crate) downlevel: wgt::DownlevelProperties, - spv_options: naga::back::spv::Options, + pub(crate) downlevel: wgt::DownlevelCapabilities, //TODO: move this behind another mutex. This would allow several methods to switch // to borrow Device immutably, such as `write_buffer`, `write_texture`, and `buffer_unmap`. - pending_writes: queue::PendingWrites, + pending_writes: queue::PendingWrites, #[cfg(feature = "trace")] pub(crate) trace: Option>, } @@ -299,72 +257,40 @@ pub enum CreateDeviceError { OutOfMemory, } -impl Device { +impl Device { #[allow(clippy::too_many_arguments)] pub(crate) fn new( - raw: B::Device, + open: hal::OpenDevice, adapter_id: Stored, - queue_group: hal::queue::QueueGroup, - mem_props: hal::adapter::MemoryProperties, - hal_limits: hal::Limits, - private_features: PrivateFeatures, - downlevel: wgt::DownlevelProperties, + alignments: hal::Alignments, + downlevel: wgt::DownlevelCapabilities, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, ) -> Result { - let cmd_allocator = command::CommandAllocator::new(queue_group.family, &raw) - .or(Err(CreateDeviceError::OutOfMemory))?; - - let mem_allocator = alloc::MemoryAllocator::new(mem_props, hal_limits); - let descriptors = descriptor::DescriptorAllocator::new(); #[cfg(not(feature = "trace"))] if let Some(_) = trace_path { log::error!("Feature 'trace' is not enabled"); } + let fence = + unsafe { open.device.create_fence() }.map_err(|_| CreateDeviceError::OutOfMemory)?; - let spv_options = { - use naga::back::spv; - let mut flags = spv::WriterFlags::empty(); - flags.set(spv::WriterFlags::DEBUG, cfg!(debug_assertions)); - //Note: we don't adjust the coordinate space, because `NDC_Y_UP` is required. - spv::Options { - lang_version: (1, 0), - //TODO: can be `None` once `spirv` is published - capabilities: Some( - [ - spv::Capability::Shader, - spv::Capability::DerivativeControl, - spv::Capability::InterpolationFunction, - spv::Capability::Matrix, - spv::Capability::ImageQuery, - spv::Capability::Sampled1D, - spv::Capability::Image1D, - spv::Capability::SampledCubeArray, - spv::Capability::ImageCubeArray, - spv::Capability::StorageImageExtendedFormats, - ] - .iter() - .cloned() - .collect(), - ), - flags, - } + let mut com_alloc = CommandAllocator { + free_encoders: Vec::new(), }; + let pending_encoder = com_alloc + .acquire_encoder(&open.device, &open.queue) + .map_err(|_| CreateDeviceError::OutOfMemory)?; + let pending_writes = queue::PendingWrites::new(pending_encoder); Ok(Self { - raw, + raw: open.device, adapter_id, - cmd_allocator, - mem_allocator: Mutex::new(mem_allocator), - desc_allocator: Mutex::new(descriptors), - queue_group, + queue: open.queue, life_guard: LifeGuard::new(""), + command_allocator: Mutex::new(com_alloc), active_submission_index: 0, - trackers: Mutex::new(TrackerSet::new(B::VARIANT)), - render_passes: Mutex::new(RenderPassLock { - render_passes: FastHashMap::default(), - framebuffers: FastHashMap::default(), - }), + fence, + trackers: Mutex::new(TrackerSet::new(A::VARIANT)), life_tracker: Mutex::new(life::LifetimeTracker::new()), temp_suspected: life::SuspectedResources::default(), #[cfg(feature = "trace")] @@ -372,7 +298,7 @@ impl Device { Ok(mut trace) => { trace.add(trace::Action::Init { desc: desc.clone(), - backend: B::VARIANT, + backend: A::VARIANT, }); Some(Mutex::new(trace)) } @@ -381,13 +307,11 @@ impl Device { None } }), - hal_limits, - private_features, + alignments, limits: desc.limits.clone(), features: desc.features, downlevel, - spv_options, - pending_writes: queue::PendingWrites::new(), + pending_writes, }) } @@ -399,14 +323,10 @@ impl Device { } } - pub(crate) fn last_completed_submission_index(&self) -> SubmissionIndex { - self.life_guard.submission_index.load(Ordering::Acquire) - } - fn lock_life_internal<'this, 'token: 'this>( - tracker: &'this Mutex>, + tracker: &'this Mutex>, _token: &mut Token<'token, Self>, - ) -> MutexGuard<'this, life::LifetimeTracker> { + ) -> MutexGuard<'this, life::LifetimeTracker> { tracker.lock() } @@ -414,13 +334,24 @@ impl Device { &'this self, //TODO: fix this - the token has to be borrowed for the lock token: &mut Token<'token, Self>, - ) -> MutexGuard<'this, life::LifetimeTracker> { + ) -> MutexGuard<'this, life::LifetimeTracker> { Self::lock_life_internal(&self.life_tracker, token) } + pub(crate) fn suspect_texture_view_for_destruction<'this, 'token: 'this>( + &'this self, + view_id: id::Valid, + token: &mut Token<'token, Self>, + ) { + self.lock_life(token) + .suspected_resources + .texture_views + .push(view_id); + } + fn maintain<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>( &'this self, - hub: &Hub, + hub: &Hub, force_wait: bool, token: &mut Token<'token, Self>, ) -> Result, WaitIdleError> { @@ -435,20 +366,33 @@ impl Device { token, ); life_tracker.triage_mapped(hub, token); - let last_done = life_tracker.triage_submissions(&self.raw, force_wait)?; + + let last_done_index = if force_wait { + let current_index = self.active_submission_index; + unsafe { + self.raw + .wait(&self.fence, current_index, CLEANUP_WAIT_MS) + .map_err(DeviceError::from)? + }; + current_index + } else { + unsafe { + self.raw + .get_fence_value(&self.fence) + .map_err(DeviceError::from)? + } + }; + + life_tracker.triage_submissions(last_done_index, &self.command_allocator); let callbacks = life_tracker.handle_mapping(hub, &self.raw, &self.trackers, token); - life_tracker.cleanup(&self.raw, &self.mem_allocator, &self.desc_allocator); + life_tracker.cleanup(&self.raw); - self.life_guard - .submission_index - .store(last_done, Ordering::Release); - self.cmd_allocator.maintain(&self.raw, last_done); Ok(callbacks) } fn untrack<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>( &'this mut self, - hub: &Hub, + hub: &Hub, trackers: &TrackerSet, mut token: &mut Token<'token, Self>, ) { @@ -517,89 +461,41 @@ impl Device { self_id: id::DeviceId, desc: &resource::BufferDescriptor, transient: bool, - ) -> Result, resource::CreateBufferError> { - debug_assert_eq!(self_id.backend(), B::VARIANT); - let (mut usage, _memory_properties) = conv::map_buffer_usage(desc.usage); + ) -> Result, resource::CreateBufferError> { + debug_assert_eq!(self_id.backend(), A::VARIANT); + let mut usage = conv::map_buffer_usage(desc.usage); + + if desc.usage.is_empty() { + return Err(resource::CreateBufferError::EmptyUsage); + } + if desc.mapped_at_creation { if desc.size % wgt::COPY_BUFFER_ALIGNMENT != 0 { return Err(resource::CreateBufferError::UnalignedSize); } if !desc.usage.contains(wgt::BufferUsage::MAP_WRITE) { // we are going to be copying into it, internally - usage |= hal::buffer::Usage::TRANSFER_DST; + usage |= hal::BufferUse::COPY_DST; } } else { // We are required to zero out (initialize) all memory. // This is done on demand using fill_buffer which requires write transfer usage! - usage |= hal::buffer::Usage::TRANSFER_DST; + usage |= hal::BufferUse::COPY_DST; } - if desc.usage.is_empty() { - return Err(resource::CreateBufferError::EmptyUsage); - } + let mut memory_flags = hal::MemoryFlag::empty(); + memory_flags.set(hal::MemoryFlag::TRANSIENT, transient); - let mem_usage = { - use gpu_alloc::UsageFlags as Uf; - use wgt::BufferUsage as Bu; - - let mut flags = Uf::empty(); - let map_flags = desc.usage & (Bu::MAP_READ | Bu::MAP_WRITE); - let map_copy_flags = - desc.usage & (Bu::MAP_READ | Bu::MAP_WRITE | Bu::COPY_SRC | Bu::COPY_DST); - if map_flags.is_empty() || !(desc.usage - map_copy_flags).is_empty() { - flags |= Uf::FAST_DEVICE_ACCESS; - } - if transient { - flags |= Uf::TRANSIENT; - } - - if !map_flags.is_empty() { - let upload_usage = Bu::MAP_WRITE | Bu::COPY_SRC; - let download_usage = Bu::MAP_READ | Bu::COPY_DST; - - flags |= Uf::HOST_ACCESS; - if desc.usage.contains(upload_usage) { - flags |= Uf::UPLOAD; - } - if desc.usage.contains(download_usage) { - flags |= Uf::DOWNLOAD; - } - - let is_native_only = self - .features - .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS); - if !is_native_only - && !upload_usage.contains(desc.usage) - && !download_usage.contains(desc.usage) - { - return Err(resource::CreateBufferError::UsageMismatch(desc.usage)); - } - } - - flags + let hal_desc = hal::BufferDescriptor { + label: desc.label.borrow_option(), + size: desc.size, + usage, + memory_flags, }; - - let mut buffer = unsafe { - self.raw - .create_buffer(desc.size.max(1), usage, hal::memory::SparseFlags::empty()) - } - .map_err(|err| match err { - hal::buffer::CreationError::OutOfMemory(_) => DeviceError::OutOfMemory, - _ => panic!("failed to create buffer: {}", err), - })?; - if let Some(ref label) = desc.label { - unsafe { self.raw.set_buffer_name(&mut buffer, label) }; - } - - let requirements = unsafe { self.raw.get_buffer_requirements(&buffer) }; - let block = self - .mem_allocator - .lock() - .allocate(&self.raw, requirements, mem_usage)?; - block.bind_buffer(&self.raw, &mut buffer)?; + let buffer = unsafe { self.raw.create_buffer(&hal_desc) }.map_err(DeviceError::from)?; Ok(resource::Buffer { - raw: Some((buffer, block)), + raw: Some(buffer), device_id: Stored { value: id::Valid(self_id), ref_count: self.life_guard.add_ref(), @@ -616,10 +512,10 @@ impl Device { fn create_texture( &self, self_id: id::DeviceId, - adapter: &crate::instance::Adapter, + adapter: &crate::instance::Adapter, desc: &resource::TextureDescriptor, - ) -> Result, resource::CreateTextureError> { - debug_assert_eq!(self_id.backend(), B::VARIANT); + ) -> Result, resource::CreateTextureError> { + debug_assert_eq!(self_id.backend(), A::VARIANT); let format_desc = desc.format.describe(); self.require_features(format_desc.required_features) @@ -659,91 +555,47 @@ impl Device { )); } - let kind = conv::map_texture_dimension_size( + conv::check_texture_dimension_size( desc.dimension, desc.size, desc.sample_count, &self.limits, )?; - let format = conv::map_texture_format(desc.format, self.private_features); - let aspects = format.surface_desc().aspects; - let usage = conv::map_texture_usage(desc.usage, aspects); - - let mip_level_count = desc.mip_level_count; - if mip_level_count == 0 - || mip_level_count > MAX_MIP_LEVELS - || mip_level_count > kind.compute_num_levels() as u32 - { - return Err(resource::CreateTextureError::InvalidMipLevelCount( - mip_level_count, - )); - } - let mut view_caps = hal::image::ViewCapabilities::empty(); - // 2D textures with array layer counts that are multiples of 6 could be cubemaps - // Following gpuweb/gpuweb#68 always add the hint in that case - if desc.dimension == TextureDimension::D2 - && desc.size.depth_or_array_layers % 6 == 0 - && (desc.size.depth_or_array_layers == 6 - || self - .downlevel - .flags - .contains(wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES)) - { - view_caps |= hal::image::ViewCapabilities::KIND_CUBE; - }; - // TODO: 2D arrays, cubemap arrays + let mips = desc.mip_level_count; + if mips == 0 || mips > hal::MAX_MIP_LEVELS || mips > desc.size.max_mips() { + return Err(resource::CreateTextureError::InvalidMipLevelCount(mips)); + } - let mut image = unsafe { - let mut image = self - .raw - .create_image( - kind, - desc.mip_level_count as hal::image::Level, - format, - hal::image::Tiling::Optimal, - usage, - hal::memory::SparseFlags::empty(), - view_caps, - ) - .map_err(|err| match err { - hal::image::CreationError::OutOfMemory(_) => DeviceError::OutOfMemory, - _ => panic!("failed to create texture: {}", err), - })?; - if let Some(ref label) = desc.label { - self.raw.set_image_name(&mut image, label); - } - image + let hal_usage = conv::map_texture_usage(desc.usage, desc.format.into()); + let hal_desc = hal::TextureDescriptor { + label: desc.label.borrow_option(), + size: desc.size, + mip_level_count: desc.mip_level_count, + sample_count: desc.sample_count, + dimension: desc.dimension, + format: desc.format, + usage: hal_usage, + memory_flags: hal::MemoryFlag::empty(), + }; + let raw = unsafe { + self.raw + .create_texture(&hal_desc) + .map_err(DeviceError::from)? }; - - let requirements = unsafe { self.raw.get_image_requirements(&image) }; - let block = self.mem_allocator.lock().allocate( - &self.raw, - requirements, - gpu_alloc::UsageFlags::FAST_DEVICE_ACCESS, - )?; - block.bind_image(&self.raw, &mut image)?; Ok(resource::Texture { - raw: Some((image, block)), + raw: Some(raw), device_id: Stored { value: id::Valid(self_id), ref_count: self.life_guard.add_ref(), }, - usage: desc.usage, - aspects, - dimension: desc.dimension, - kind, - format: desc.format, + desc: desc.map_label(|_| ()), + hal_usage, format_features, - framebuffer_attachment: hal::image::FramebufferAttachment { - usage, - view_caps, - format, - }, full_range: TextureSelector { - levels: 0..desc.mip_level_count as hal::image::Level, - layers: 0..kind.num_layers(), + levels: 0..desc.mip_level_count, + layers: 0..desc.array_layer_count(), }, life_guard: LifeGuard::new(desc.label.borrow_or_default()), }) @@ -751,108 +603,102 @@ impl Device { fn create_texture_view( &self, - texture: &resource::Texture, + texture: &resource::Texture, texture_id: id::TextureId, desc: &resource::TextureViewDescriptor, - ) -> Result, resource::CreateTextureViewError> { - let &(ref texture_raw, _) = texture + ) -> Result, resource::CreateTextureViewError> { + let texture_raw = texture .raw .as_ref() .ok_or(resource::CreateTextureViewError::InvalidTexture)?; - let view_dim = - match desc.dimension { - Some(dim) => { - use hal::image::Kind; - - let required_tex_dim = dim.compatible_texture_dimension(); - - if required_tex_dim != texture.dimension { - return Err( - resource::CreateTextureViewError::InvalidTextureViewDimension { - view: dim, - image: texture.dimension, - }, - ); - } - - if let Kind::D2(_, _, depth, _) = texture.kind { - match dim { - TextureViewDimension::Cube if depth != 6 => { - return Err( - resource::CreateTextureViewError::InvalidCubemapTextureDepth { - depth, - }, - ) - } - TextureViewDimension::CubeArray if depth % 6 != 0 => return Err( - resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth { - depth, - }, - ), - _ => {} - } - } - - dim + let view_dim = match desc.dimension { + Some(dim) => { + if texture.desc.dimension != dim.compatible_texture_dimension() { + return Err( + resource::CreateTextureViewError::InvalidTextureViewDimension { + view: dim, + texture: texture.desc.dimension, + }, + ); } - None => match texture.kind { - hal::image::Kind::D1(..) => wgt::TextureViewDimension::D1, - hal::image::Kind::D2(_, _, depth, _) - if depth > 1 && desc.range.array_layer_count.is_none() => - { - wgt::TextureViewDimension::D2Array - } - hal::image::Kind::D2(..) => wgt::TextureViewDimension::D2, - hal::image::Kind::D3(..) => wgt::TextureViewDimension::D3, - }, - }; + dim + } + None => match texture.desc.dimension { + wgt::TextureDimension::D1 => wgt::TextureViewDimension::D1, + wgt::TextureDimension::D2 + if texture.desc.size.depth_or_array_layers > 1 + && desc.range.array_layer_count.is_none() => + { + wgt::TextureViewDimension::D2Array + } + wgt::TextureDimension::D2 => wgt::TextureViewDimension::D2, + wgt::TextureDimension::D3 => wgt::TextureViewDimension::D3, + }, + }; let required_level_count = desc.range.base_mip_level + desc.range.mip_level_count.map_or(1, |count| count.get()); - let required_layer_count = desc.range.base_array_layer - + desc.range.array_layer_count.map_or(1, |count| count.get()); + let required_layer_count = match desc.range.array_layer_count { + Some(count) => desc.range.base_array_layer + count.get(), + None => texture.desc.array_layer_count(), + }; let level_end = texture.full_range.levels.end; let layer_end = texture.full_range.layers.end; - if required_level_count > level_end as u32 { + if required_level_count > level_end { return Err(resource::CreateTextureViewError::TooManyMipLevels { requested: required_level_count, total: level_end, }); } - if required_layer_count > layer_end as u32 { + if required_layer_count > layer_end { return Err(resource::CreateTextureViewError::TooManyArrayLayers { requested: required_layer_count, total: layer_end, }); }; - let aspects = match desc.range.aspect { - wgt::TextureAspect::All => texture.aspects, - wgt::TextureAspect::DepthOnly => hal::format::Aspects::DEPTH, - wgt::TextureAspect::StencilOnly => hal::format::Aspects::STENCIL, - }; - if !texture.aspects.contains(aspects) { + match view_dim { + TextureViewDimension::Cube if required_layer_count != 6 => { + return Err( + resource::CreateTextureViewError::InvalidCubemapTextureDepth { + depth: required_layer_count, + }, + ) + } + TextureViewDimension::CubeArray if required_layer_count % 6 != 0 => { + return Err( + resource::CreateTextureViewError::InvalidCubemapArrayTextureDepth { + depth: required_layer_count, + }, + ) + } + _ => {} + } + + let full_aspect = hal::FormatAspect::from(texture.desc.format); + let select_aspect = hal::FormatAspect::from(desc.range.aspect); + if (full_aspect & select_aspect).is_empty() { return Err(resource::CreateTextureViewError::InvalidAspect { - requested: aspects, - total: texture.aspects, + texture_format: texture.desc.format, + requested_aspect: desc.range.aspect, }); } let end_level = desc .range .mip_level_count - .map_or(level_end, |_| required_level_count as u8); + .map_or(level_end, |_| required_level_count); let end_layer = desc .range .array_layer_count - .map_or(layer_end, |_| required_layer_count as u16); + .map_or(layer_end, |_| required_layer_count); let selector = TextureSelector { - levels: desc.range.base_mip_level as u8..end_level, - layers: desc.range.base_array_layer as u16..end_layer, + levels: desc.range.base_mip_level..end_level, + layers: desc.range.base_array_layer..end_layer, }; - let view_layer_count = (selector.layers.end - selector.layers.start) as u32; + let view_layer_count = selector.layers.end - selector.layers.start; let layer_check_ok = match view_dim { wgt::TextureViewDimension::D1 | wgt::TextureViewDimension::D2 @@ -868,57 +714,54 @@ impl Device { }); } - let format = desc.format.unwrap_or(texture.format); - let range = hal::image::SubresourceRange { - aspects, - level_start: desc.range.base_mip_level as _, - level_count: desc.range.mip_level_count.map(|v| v.get() as _), - layer_start: desc.range.base_array_layer as _, - layer_count: desc.range.array_layer_count.map(|v| v.get() as _), + let mut extent = texture + .desc + .mip_level_size(desc.range.base_mip_level) + .unwrap(); + if view_dim != wgt::TextureViewDimension::D3 { + extent.depth_or_array_layers = view_layer_count; + } + let format = desc.format.unwrap_or(texture.desc.format); + if format != texture.desc.format { + return Err(resource::CreateTextureViewError::FormatReinterpretation { + texture: texture.desc.format, + view: format, + }); + } + + let hal_desc = hal::TextureViewDescriptor { + label: desc.label.borrow_option(), + format, + dimension: view_dim, + usage: texture.hal_usage, // pass-through + range: desc.range.clone(), }; - let hal_extent = texture - .kind - .extent() - .at_level(desc.range.base_mip_level as _); let raw = unsafe { self.raw - .create_image_view( - texture_raw, - conv::map_texture_view_dimension(view_dim), - conv::map_texture_format(format, self.private_features), - hal::format::Swizzle::NO, - // conservatively assume the same usage - conv::map_texture_usage(texture.usage, aspects), - range, - ) - .or(Err(resource::CreateTextureViewError::OutOfMemory))? + .create_texture_view(texture_raw, &hal_desc) + .map_err(|_| resource::CreateTextureViewError::OutOfMemory)? }; Ok(resource::TextureView { - inner: resource::TextureViewInner::Native { - raw, - source_id: Stored { - value: id::Valid(texture_id), - ref_count: texture.life_guard.add_ref(), - }, + raw, + source: resource::TextureViewSource::Native(Stored { + value: id::Valid(texture_id), + ref_count: texture.life_guard.add_ref(), + }), + desc: resource::HalTextureViewDescriptor { + format: hal_desc.format, + dimension: hal_desc.dimension, + range: hal_desc.range, }, - aspects, - format: texture.format, format_features: texture.format_features, - dimension: view_dim, - extent: wgt::Extent3d { - width: hal_extent.width, - height: hal_extent.height, - depth_or_array_layers: view_layer_count, - }, - samples: texture.kind.num_samples(), - framebuffer_attachment: texture.framebuffer_attachment.clone(), + extent, + samples: texture.desc.sample_count, // once a storage - forever a storage - sampled_internal_use: if texture.usage.contains(wgt::TextureUsage::STORAGE) { - resource::TextureUse::SAMPLED | resource::TextureUse::STORAGE_LOAD + sampled_internal_use: if texture.desc.usage.contains(wgt::TextureUsage::STORAGE) { + hal::TextureUse::SAMPLED | hal::TextureUse::STORAGE_LOAD } else { - resource::TextureUse::SAMPLED + hal::TextureUse::SAMPLED }, selector, life_guard: LifeGuard::new(desc.label.borrow_or_default()), @@ -929,7 +772,7 @@ impl Device { &self, self_id: id::DeviceId, desc: &resource::SamplerDescriptor, - ) -> Result, resource::CreateSamplerError> { + ) -> Result, resource::CreateSamplerError> { if desc .address_modes .iter() @@ -938,14 +781,24 @@ impl Device { self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?; } - let actual_clamp = if let Some(clamp) = desc.anisotropy_clamp { + let lod_clamp = if desc.lod_min_clamp > 0.0 || desc.lod_max_clamp < 32.0 { + Some(desc.lod_min_clamp..desc.lod_max_clamp) + } else { + None + }; + + let anisotropy_clamp = if let Some(clamp) = desc.anisotropy_clamp { let clamp = clamp.get(); - let valid_clamp = clamp <= MAX_ANISOTROPY && conv::is_power_of_two(clamp as u32); + let valid_clamp = clamp <= hal::MAX_ANISOTROPY && conv::is_power_of_two(clamp as u32); if !valid_clamp { return Err(resource::CreateSamplerError::InvalidClamp(clamp)); } - if self.private_features.anisotropic_filtering { - Some(clamp) + if self + .downlevel + .flags + .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING) + { + std::num::NonZeroU8::new(clamp) } else { None } @@ -953,44 +806,24 @@ impl Device { None }; - let border = match desc.border_color { - None | Some(wgt::SamplerBorderColor::TransparentBlack) => { - hal::image::BorderColor::TransparentBlack - } - Some(wgt::SamplerBorderColor::OpaqueBlack) => hal::image::BorderColor::OpaqueBlack, - Some(wgt::SamplerBorderColor::OpaqueWhite) => hal::image::BorderColor::OpaqueWhite, - }; - - let filtering = [desc.min_filter, desc.mag_filter, desc.mipmap_filter] - .contains(&wgt::FilterMode::Linear); - - let info = hal::image::SamplerDesc { - min_filter: conv::map_filter(desc.min_filter), - mag_filter: conv::map_filter(desc.mag_filter), - mip_filter: conv::map_filter(desc.mipmap_filter), - reduction_mode: hal::image::ReductionMode::WeightedAverage, - wrap_mode: ( - conv::map_wrap(desc.address_modes[0]), - conv::map_wrap(desc.address_modes[1]), - conv::map_wrap(desc.address_modes[2]), - ), - lod_bias: hal::image::Lod(0.0), - lod_range: hal::image::Lod(desc.lod_min_clamp)..hal::image::Lod(desc.lod_max_clamp), - comparison: desc.compare.map(conv::map_compare_function), - border, - normalized: true, - anisotropy_clamp: actual_clamp, + //TODO: check for wgt::DownlevelFlags::COMPARISON_SAMPLERS + + let hal_desc = hal::SamplerDescriptor { + label: desc.label.borrow_option(), + address_modes: desc.address_modes, + mag_filter: desc.mag_filter, + min_filter: desc.min_filter, + mipmap_filter: desc.mipmap_filter, + lod_clamp, + compare: desc.compare, + anisotropy_clamp, + border_color: desc.border_color, }; let raw = unsafe { - self.raw.create_sampler(&info).map_err(|err| match err { - hal::device::AllocationError::OutOfMemory(_) => { - resource::CreateSamplerError::Device(DeviceError::OutOfMemory) - } - hal::device::AllocationError::TooManyObjects => { - resource::CreateSamplerError::TooManyObjects - } - })? + self.raw + .create_sampler(&hal_desc) + .map_err(DeviceError::from)? }; Ok(resource::Sampler { raw, @@ -999,8 +832,9 @@ impl Device { ref_count: self.life_guard.add_ref(), }, life_guard: LifeGuard::new(desc.label.borrow_or_default()), - comparison: info.comparison.is_some(), - filtering, + comparison: desc.compare.is_some(), + filtering: desc.min_filter == wgt::FilterMode::Linear + || desc.mag_filter == wgt::FilterMode::Linear, }) } @@ -1009,9 +843,8 @@ impl Device { self_id: id::DeviceId, desc: &pipeline::ShaderModuleDescriptor<'a>, source: pipeline::ShaderModuleSource<'a>, - ) -> Result, pipeline::CreateShaderModuleError> { - // First, try to produce a Naga module. - let (spv, module) = match source { + ) -> Result, pipeline::CreateShaderModuleError> { + let module = match source { pipeline::ShaderModuleSource::SpirV(spv) => { profiling::scope!("naga::spv::parse"); // Parse the given shader code and store its representation. @@ -1021,163 +854,84 @@ impl Device { flow_graph_dump_prefix: None, }; let parser = naga::front::spv::Parser::new(spv.iter().cloned(), &options); - let module = match parser.parse() { - Ok(module) => Some(module), + match parser.parse() { + Ok(module) => module, Err(err) => { log::warn!( "Failed to parse shader SPIR-V code for {:?}: {:?}", desc.label, err ); - if desc.flags.contains(wgt::ShaderFlags::VALIDATION) { - return Err(pipeline::CreateShaderModuleError::Parsing); - } - log::warn!("\tProceeding unsafely without validation"); - None + return Err(pipeline::CreateShaderModuleError::Parsing); } - }; - (Some(spv), module) + } } pipeline::ShaderModuleSource::Wgsl(code) => { profiling::scope!("naga::wgsl::parse_str"); // TODO: refactor the corresponding Naga error to be owned, and then // display it instead of unwrapping match naga::front::wgsl::parse_str(&code) { - Ok(module) => (None, Some(module)), + Ok(module) => module, Err(err) => { log::error!("Failed to parse WGSL code for {:?}: {}", desc.label, err); return Err(pipeline::CreateShaderModuleError::Parsing); } } } - pipeline::ShaderModuleSource::Naga(module) => (None, Some(module)), + pipeline::ShaderModuleSource::Naga(module) => module, }; - let (naga_result, interface) = match module { - // If succeeded, then validate it and attempt to give it to gfx-hal directly. - Some(module) if desc.flags.contains(wgt::ShaderFlags::VALIDATION) || spv.is_none() => { - use naga::valid::Capabilities as Caps; - profiling::scope!("naga::validate"); + use naga::valid::Capabilities as Caps; + profiling::scope!("naga::validate"); - let mut caps = Caps::empty(); - caps.set( - Caps::PUSH_CONSTANT, - self.features.contains(wgt::Features::PUSH_CONSTANTS), - ); - caps.set( - Caps::FLOAT64, - self.features.contains(wgt::Features::SHADER_FLOAT64), - ); - let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) - .validate(&module)?; - let interface = validation::Interface::new(&module, &info); - let shader = hal::device::NagaShader { module, info }; - - let naga_result = if desc - .flags - .contains(wgt::ShaderFlags::EXPERIMENTAL_TRANSLATION) - || !cfg!(feature = "cross") - { - match unsafe { self.raw.create_shader_module_from_naga(shader) } { - Ok(raw) => Ok(raw), - Err((hal::device::ShaderError::CompilationFailed(msg), shader)) => { - log::warn!("Shader module compilation failed: {}", msg); - Err(Some(shader)) - } - Err((_, shader)) => Err(Some(shader)), - } - } else { - Err(Some(shader)) - }; - (naga_result, Some(interface)) - } - _ => (Err(None), None), - }; + let mut caps = Caps::empty(); + caps.set( + Caps::PUSH_CONSTANT, + self.features.contains(wgt::Features::PUSH_CONSTANTS), + ); + caps.set( + Caps::FLOAT64, + self.features.contains(wgt::Features::SHADER_FLOAT64), + ); + let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) + .validate(&module)?; + let interface = validation::Interface::new(&module, &info); + let hal_shader = hal::NagaShader { module, info }; - // Otherwise, fall back to SPIR-V. - let spv_result = match naga_result { - Ok(raw) => Ok(raw), - Err(maybe_shader) => { - let spv = match spv { - Some(data) => Ok(data), - None => { - // Produce a SPIR-V from the Naga module - profiling::scope!("naga::wpv::write_vec"); - let shader = maybe_shader.unwrap(); - naga::back::spv::write_vec(&shader.module, &shader.info, &self.spv_options) - .map(Cow::Owned) + let hal_desc = hal::ShaderModuleDescriptor { + label: desc.label.borrow_option(), + }; + let raw = match unsafe { self.raw.create_shader_module(&hal_desc, hal_shader) } { + Ok(raw) => raw, + Err(error) => { + return Err(match error { + hal::ShaderError::Device(error) => { + pipeline::CreateShaderModuleError::Device(error.into()) } - }; - match spv { - Ok(data) => unsafe { self.raw.create_shader_module(&data) }, - Err(e) => Err(hal::device::ShaderError::CompilationFailed(format!( - "{}", - e - ))), - } + hal::ShaderError::Compilation(ref msg) => { + log::error!("Shader error: {}", msg); + pipeline::CreateShaderModuleError::Generation + } + }) } }; Ok(pipeline::ShaderModule { - raw: match spv_result { - Ok(raw) => raw, - Err(hal::device::ShaderError::OutOfMemory(_)) => { - return Err(DeviceError::OutOfMemory.into()); - } - Err(error) => { - log::error!("Shader error: {}", error); - return Err(pipeline::CreateShaderModuleError::Generation); - } - }, + raw, device_id: Stored { value: id::Valid(self_id), ref_count: self.life_guard.add_ref(), }, interface, #[cfg(debug_assertions)] - label: desc.label.to_string_or_default(), + label: desc.label.borrow_or_default().to_string(), }) } - /// Create a compatible render pass with a given key. - /// - /// This functions doesn't consider the following aspects for compatibility: - /// - image layouts - /// - resolve attachments - fn create_compatible_render_pass( - &self, - key: &RenderPassKey, - ) -> Result { - let mut color_ids = [(0, hal::image::Layout::ColorAttachmentOptimal); MAX_COLOR_TARGETS]; - for (index, color) in color_ids[..key.colors.len()].iter_mut().enumerate() { - color.0 = index; - } - let depth_id = key.depth_stencil.as_ref().map(|_| { - ( - key.colors.len(), - hal::image::Layout::DepthStencilAttachmentOptimal, - ) - }); - - let subpass = hal::pass::SubpassDesc { - colors: &color_ids[..key.colors.len()], - depth_stencil: depth_id.as_ref(), - inputs: &[], - resolves: &[], - preserves: &[], - }; - let all = key.all().map(|&(ref at, _)| at.clone()); - - unsafe { - self.raw - .create_render_pass(all, iter::once(subpass), iter::empty()) - } - } - fn deduplicate_bind_group_layout( self_id: id::DeviceId, entry_map: &binding_model::BindEntryMap, - guard: &Storage, id::BindGroupLayoutId>, + guard: &Storage, id::BindGroupLayoutId>, ) -> Option { guard .iter(self_id.backend()) @@ -1189,9 +943,9 @@ impl Device { } fn get_introspection_bind_group_layouts<'a>( - pipeline_layout: &binding_model::PipelineLayout, - bgl_guard: &'a Storage, id::BindGroupLayoutId>, - ) -> ArrayVec<[&'a binding_model::BindEntryMap; MAX_BIND_GROUPS]> { + pipeline_layout: &binding_model::PipelineLayout, + bgl_guard: &'a Storage, id::BindGroupLayoutId>, + ) -> ArrayVec<[&'a binding_model::BindEntryMap; hal::MAX_BIND_GROUPS]> { pipeline_layout .bind_group_layout_ids .iter() @@ -1204,52 +958,29 @@ impl Device { self_id: id::DeviceId, label: Option<&str>, entry_map: binding_model::BindEntryMap, - ) -> Result, binding_model::CreateBindGroupLayoutError> { - let mut desc_count = descriptor::DescriptorTotalCount::default(); + ) -> Result, binding_model::CreateBindGroupLayoutError> { for entry in entry_map.values() { use wgt::BindingType as Bt; let mut required_features = wgt::Features::empty(); - let (counter, array_feature, is_writable_storage) = match entry.ty { + let (array_feature, is_writable_storage) = match entry.ty { Bt::Buffer { ty: wgt::BufferBindingType::Uniform, has_dynamic_offset: false, min_binding_size: _, - } => ( - &mut desc_count.uniform_buffer, - Some(wgt::Features::BUFFER_BINDING_ARRAY), - false, - ), + } => (Some(wgt::Features::BUFFER_BINDING_ARRAY), false), Bt::Buffer { ty: wgt::BufferBindingType::Uniform, has_dynamic_offset: true, min_binding_size: _, - } => ( - &mut desc_count.uniform_buffer_dynamic, - Some(wgt::Features::BUFFER_BINDING_ARRAY), - false, - ), + } => (Some(wgt::Features::BUFFER_BINDING_ARRAY), false), Bt::Buffer { ty: wgt::BufferBindingType::Storage { read_only }, - has_dynamic_offset, - min_binding_size: _, - } => ( - if has_dynamic_offset { - &mut desc_count.storage_buffer_dynamic - } else { - &mut desc_count.storage_buffer - }, - Some(wgt::Features::BUFFER_BINDING_ARRAY), - !read_only, - ), - Bt::Sampler { .. } => (&mut desc_count.sampler, None, false), - Bt::Texture { .. } => ( - &mut desc_count.sampled_image, - Some(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY), - false, - ), + .. + } => (Some(wgt::Features::BUFFER_BINDING_ARRAY), !read_only), + Bt::Sampler { .. } => (None, false), + Bt::Texture { .. } => (Some(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY), false), Bt::StorageTexture { access, .. } => ( - &mut desc_count.storage_image, None, match access { wgt::StorageTextureAccess::ReadOnly => false, @@ -1263,19 +994,15 @@ impl Device { ), }; - *counter += match entry.count { - // Validate the count parameter - Some(count) => { - required_features |= array_feature - .ok_or(binding_model::BindGroupLayoutEntryError::ArrayUnsupported) - .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry { - binding: entry.binding, - error, - })?; - count.get() - } - None => 1, - }; + // Validate the count parameter + if entry.count.is_some() { + required_features |= array_feature + .ok_or(binding_model::BindGroupLayoutEntryError::ArrayUnsupported) + .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry { + binding: entry.binding, + error, + })?; + } if is_writable_storage && entry.visibility.contains(wgt::ShaderStage::VERTEX) { required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE; } @@ -1288,27 +1015,15 @@ impl Device { })?; } - let raw_bindings = entry_map - .values() - .map(|entry| hal::pso::DescriptorSetLayoutBinding { - binding: entry.binding, - ty: conv::map_binding_type(entry), - count: entry - .count - .map_or(1, |v| v.get() as hal::pso::DescriptorArrayIndex), //TODO: consolidate - stage_flags: conv::map_shader_stage_flags(entry.visibility), - immutable_samplers: false, // TODO - }); + let hal_bindings = entry_map.values().cloned().collect::>(); + let hal_desc = hal::BindGroupLayoutDescriptor { + label, + entries: &hal_bindings, + }; let raw = unsafe { - let mut raw_layout = self - .raw - .create_descriptor_set_layout(raw_bindings, iter::empty()) - .or(Err(DeviceError::OutOfMemory))?; - if let Some(label) = label { - self.raw - .set_descriptor_set_layout_name(&mut raw_layout, label); - } - raw_layout + self.raw + .create_bind_group_layout(&hal_desc) + .map_err(DeviceError::from)? }; let mut count_validator = binding_model::BindingTypeMaxCountValidator::default(); @@ -1328,7 +1043,6 @@ impl Device { ref_count: self.life_guard.add_ref(), }, multi_ref_count: MultiRefCount::new(), - desc_count, dynamic_count: entry_map .values() .filter(|b| b.ty.has_dynamic_offset()) @@ -1341,16 +1055,16 @@ impl Device { } #[allow(clippy::too_many_arguments)] - fn create_buffer_descriptor<'a>( + fn create_buffer_binding<'a>( bb: &binding_model::BufferBinding, binding: u32, decl: &wgt::BindGroupLayoutEntry, used_buffer_ranges: &mut Vec>, dynamic_binding_info: &mut Vec, used: &mut TrackerSet, - storage: &'a Storage, id::BufferId>, + storage: &'a Storage, id::BufferId>, limits: &wgt::Limits, - ) -> Result, binding_model::CreateBindGroupError> { + ) -> Result, binding_model::CreateBindGroupError> { use crate::binding_model::CreateBindGroupError as Error; let (binding_ty, dynamic, min_size) = match decl.ty { @@ -1370,15 +1084,15 @@ impl Device { let (pub_usage, internal_use, range_limit) = match binding_ty { wgt::BufferBindingType::Uniform => ( wgt::BufferUsage::UNIFORM, - resource::BufferUse::UNIFORM, + hal::BufferUse::UNIFORM, limits.max_uniform_buffer_binding_size, ), wgt::BufferBindingType::Storage { read_only } => ( wgt::BufferUsage::STORAGE, if read_only { - resource::BufferUse::STORAGE_LOAD + hal::BufferUse::STORAGE_LOAD } else { - resource::BufferUse::STORAGE_STORE + hal::BufferUse::STORAGE_STORE }, limits.max_storage_buffer_binding_size, ), @@ -1393,7 +1107,7 @@ impl Device { .use_extend(storage, bb.buffer_id, (), internal_use) .map_err(|_| Error::InvalidBuffer(bb.buffer_id))?; check_buffer_usage(buffer.usage, pub_usage)?; - let &(ref buffer_raw, _) = buffer + let raw_buffer = buffer .raw .as_ref() .ok_or(Error::InvalidBuffer(bb.buffer_id))?; @@ -1447,21 +1161,21 @@ impl Device { kind: MemoryInitKind::NeedsInitializedMemory, }); - let sub_range = hal::buffer::SubRange { + Ok(hal::BufferBinding { + buffer: raw_buffer, offset: bb.offset, - size: Some(bind_size), - }; - Ok(hal::pso::Descriptor::Buffer(buffer_raw, sub_range)) + size: bb.size, + }) } fn create_bind_group( &self, self_id: id::DeviceId, - layout: &binding_model::BindGroupLayout, + layout: &binding_model::BindGroupLayout, desc: &binding_model::BindGroupDescriptor, - hub: &Hub, - token: &mut Token>, - ) -> Result, binding_model::CreateBindGroupError> { + hub: &Hub, + token: &mut Token>, + ) -> Result, binding_model::CreateBindGroupError> { use crate::binding_model::{BindingResource as Br, CreateBindGroupError as Error}; { // Check that the number of entries in the descriptor matches @@ -1477,17 +1191,18 @@ impl Device { // Record binding info for dynamic offset validation let mut dynamic_binding_info = Vec::new(); // fill out the descriptors - let mut used = TrackerSet::new(B::VARIANT); + let mut used = TrackerSet::new(A::VARIANT); let (buffer_guard, mut token) = hub.buffers.read(token); let (texture_guard, mut token) = hub.textures.read(&mut token); //skip token let (texture_view_guard, mut token) = hub.texture_views.read(&mut token); let (sampler_guard, _) = hub.samplers.read(&mut token); - // `BTreeMap` has ordered bindings as keys, which allows us to coalesce - // the descriptor writes into a single transaction. - let mut write_map = BTreeMap::new(); let mut used_buffer_ranges = Vec::new(); + let mut hal_entries = Vec::with_capacity(desc.entries.len()); + let mut hal_buffers = Vec::new(); + let mut hal_samplers = Vec::new(); + let mut hal_textures = Vec::new(); for entry in desc.entries.iter() { let binding = entry.binding; // Find the corresponding declaration in the layout @@ -1495,9 +1210,9 @@ impl Device { .entries .get(&binding) .ok_or(Error::MissingBindingDeclaration(binding))?; - let descriptors: SmallVec<[_; 1]> = match entry.resource { + let res_index = match entry.resource { Br::Buffer(ref bb) => { - let buffer_desc = Self::create_buffer_descriptor( + let bb = Self::create_buffer_binding( &bb, binding, &decl, @@ -1507,7 +1222,10 @@ impl Device { &*buffer_guard, &self.limits, )?; - SmallVec::from([buffer_desc]) + + let res_index = hal_buffers.len(); + hal_buffers.push(bb); + res_index } Br::BufferArray(ref bindings_array) => { if let Some(count) = decl.count { @@ -1523,21 +1241,21 @@ impl Device { return Err(Error::SingleBindingExpected); } - bindings_array - .iter() - .map(|bb| { - Self::create_buffer_descriptor( - &bb, - binding, - &decl, - &mut used_buffer_ranges, - &mut dynamic_binding_info, - &mut used, - &*buffer_guard, - &self.limits, - ) - }) - .collect::>()? + let res_index = hal_buffers.len(); + for bb in bindings_array.iter() { + let bb = Self::create_buffer_binding( + bb, + binding, + &decl, + &mut used_buffer_ranges, + &mut dynamic_binding_info, + &mut used, + &*buffer_guard, + &self.limits, + )?; + hal_buffers.push(bb); + } + res_index } Br::Sampler(id) => { match decl.ty { @@ -1567,7 +1285,9 @@ impl Device { }); } - SmallVec::from([hal::pso::Descriptor::Sampler(&sampler.raw)]) + let res_index = hal_samplers.len(); + hal_samplers.push(&sampler.raw); + res_index } _ => { return Err(Error::WrongBindingType { @@ -1583,7 +1303,7 @@ impl Device { .views .use_extend(&*texture_view_guard, id, (), ()) .map_err(|_| Error::InvalidTextureView(id))?; - let format_info = view.format.describe(); + let format_info = view.desc.format.describe(); let (pub_usage, internal_use) = match decl.ty { wgt::BindingType::Texture { sample_type, @@ -1595,7 +1315,7 @@ impl Device { return Err(Error::InvalidTextureMultisample { binding, layout_multisampled: multisampled, - view_samples: view.samples as u32, + view_samples: view.samples, }); } match (sample_type, format_info.sample_type, view.format_features.filterable ) { @@ -1613,17 +1333,17 @@ impl Device { (Tst::Float { .. }, Tst::Depth, ..) => {} _ => { return Err(Error::InvalidTextureSampleType { - binding, - layout_sample_type: sample_type, - view_format: view.format, - }) - }, + binding, + layout_sample_type: sample_type, + view_format: view.desc.format, + }) + } } - if view_dimension != view.dimension { + if view_dimension != view.desc.dimension { return Err(Error::InvalidTextureDimension { binding, layout_dimension: view_dimension, - view_dimension: view.dimension, + view_dimension: view.desc.dimension, }); } (wgt::TextureUsage::SAMPLED, view.sampled_internal_use) @@ -1633,38 +1353,37 @@ impl Device { format, view_dimension, } => { - if format != view.format { + if format != view.desc.format { return Err(Error::InvalidStorageTextureFormat { binding, layout_format: format, - view_format: view.format, + view_format: view.desc.format, }); } - if view_dimension != view.dimension { + if view_dimension != view.desc.dimension { return Err(Error::InvalidTextureDimension { binding, layout_dimension: view_dimension, - view_dimension: view.dimension, + view_dimension: view.desc.dimension, }); } let internal_use = match access { wgt::StorageTextureAccess::ReadOnly => { - resource::TextureUse::STORAGE_LOAD + hal::TextureUse::STORAGE_LOAD } wgt::StorageTextureAccess::WriteOnly => { - resource::TextureUse::STORAGE_STORE + hal::TextureUse::STORAGE_STORE } wgt::StorageTextureAccess::ReadWrite => { if !view.format_features.flags.contains( wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE, ) { return Err(Error::StorageReadWriteNotSupported( - view.format, + view.desc.format, )); } - resource::TextureUse::STORAGE_STORE - | resource::TextureUse::STORAGE_LOAD + hal::TextureUse::STORAGE_STORE | hal::TextureUse::STORAGE_LOAD } }; (wgt::TextureUsage::STORAGE, internal_use) @@ -1676,17 +1395,14 @@ impl Device { "SampledTexture, ReadonlyStorageTexture or WriteonlyStorageTexture", }), }; - if view - .aspects - .contains(hal::format::Aspects::DEPTH | hal::format::Aspects::STENCIL) + + if hal::FormatAspect::from(view.desc.format) + .contains(hal::FormatAspect::DEPTH | hal::FormatAspect::STENCIL) { return Err(Error::DepthStencilAspect); } - match view.inner { - resource::TextureViewInner::Native { - ref raw, - ref source_id, - } => { + match view.source { + resource::TextureViewSource::Native(ref source_id) => { // Careful here: the texture may no longer have its own ref count, // if it was deleted by the user. let texture = &texture_guard[source_id.value]; @@ -1698,15 +1414,19 @@ impl Device { internal_use, ) .map_err(UsageConflict::from)?; - check_texture_usage(texture.usage, pub_usage)?; - let image_layout = - conv::map_texture_state(internal_use, view.aspects).1; - SmallVec::from([hal::pso::Descriptor::Image(raw, image_layout)]) + check_texture_usage(texture.desc.usage, pub_usage)?; } - resource::TextureViewInner::SwapChain { .. } => { + resource::TextureViewSource::SwapChain(_) => { return Err(Error::SwapChainImage); } } + + let res_index = hal_textures.len(); + hal_textures.push(hal::TextureBinding { + view: &view.raw, + usage: internal_use, + }); + res_index } Br::TextureViewArray(ref bindings_array) => { if let Some(count) = decl.count { @@ -1722,87 +1442,84 @@ impl Device { return Err(Error::SingleBindingExpected); } - bindings_array - .iter() - .map(|&id| { - let view = used - .views - .use_extend(&*texture_view_guard, id, (), ()) - .map_err(|_| Error::InvalidTextureView(id))?; - let (pub_usage, internal_use) = match decl.ty { - wgt::BindingType::Texture { .. } => { - (wgt::TextureUsage::SAMPLED, view.sampled_internal_use) - } - _ => { - return Err(Error::WrongBindingType { - binding, - actual: decl.ty, - expected: "SampledTextureArray", - }) - } - }; - match view.inner { - resource::TextureViewInner::Native { - ref raw, - ref source_id, - } => { - // Careful here: the texture may no longer have its own ref count, - // if it was deleted by the user. - let texture = &texture_guard[source_id.value]; - used.textures - .change_extend( - source_id.value, - &source_id.ref_count, - view.selector.clone(), - internal_use, - ) - .map_err(UsageConflict::from)?; - check_texture_usage(texture.usage, pub_usage)?; - let image_layout = - conv::map_texture_state(internal_use, view.aspects).1; - Ok(hal::pso::Descriptor::Image(raw, image_layout)) - } - resource::TextureViewInner::SwapChain { .. } => { - Err(Error::SwapChainImage) - } + let res_index = hal_textures.len(); + for &id in bindings_array.iter() { + let view = used + .views + .use_extend(&*texture_view_guard, id, (), ()) + .map_err(|_| Error::InvalidTextureView(id))?; + let (pub_usage, internal_use) = match decl.ty { + wgt::BindingType::Texture { .. } => { + (wgt::TextureUsage::SAMPLED, view.sampled_internal_use) } - }) - .collect::>()? + _ => { + return Err(Error::WrongBindingType { + binding, + actual: decl.ty, + expected: "SampledTextureArray", + }) + } + }; + + match view.source { + resource::TextureViewSource::Native(ref source_id) => { + // Careful here: the texture may no longer have its own ref count, + // if it was deleted by the user. + let texture = &texture_guard[source_id.value]; + used.textures + .change_extend( + source_id.value, + &source_id.ref_count, + view.selector.clone(), + internal_use, + ) + .map_err(UsageConflict::from)?; + check_texture_usage(texture.desc.usage, pub_usage)?; + } + resource::TextureViewSource::SwapChain(_) => { + return Err(Error::SwapChainImage); + } + } + + hal_textures.push(hal::TextureBinding { + view: &view.raw, + usage: internal_use, + }); + } + + res_index } }; - if write_map.insert(binding, descriptors).is_some() { - return Err(Error::DuplicateBinding(binding)); - } - } - - let mut desc_sets = - self.desc_allocator - .lock() - .allocate(&self.raw, &layout.raw, &layout.desc_count, 1)?; - let mut desc_set = desc_sets.pop().unwrap(); - // Set the descriptor set's label for easier debugging. - if let Some(label) = desc.label.as_ref() { - unsafe { - self.raw.set_descriptor_set_name(desc_set.raw_mut(), &label); - } + hal_entries.push(hal::BindGroupEntry { + binding, + resource_index: res_index as u32, + }); } - if let Some(start_binding) = write_map.keys().next().cloned() { - let descriptors = write_map.into_iter().flat_map(|(_, list)| list); - unsafe { - let write = hal::pso::DescriptorSetWrite { - set: desc_set.raw_mut(), - binding: start_binding, - array_offset: 0, - descriptors, - }; - self.raw.write_descriptor_set(write); + hal_entries.sort_by_key(|entry| entry.binding); + for (a, b) in hal_entries.iter().zip(hal_entries.iter().skip(1)) { + if a.binding == b.binding { + return Err(Error::DuplicateBinding(a.binding)); } } + let hal_desc = hal::BindGroupDescriptor { + label: desc.label.borrow_option(), + layout: &layout.raw, + entries: &hal_entries, + buffers: &hal_buffers, + samplers: &hal_samplers, + textures: &hal_textures, + }; + let raw = unsafe { + self.raw + .create_bind_group(&hal_desc) + .map_err(DeviceError::from)? + }; + Ok(binding_model::BindGroup { - raw: desc_set, + raw, device_id: Stored { value: id::Valid(self_id), ref_count: self.life_guard.add_ref(), @@ -1819,8 +1536,8 @@ impl Device { &self, self_id: id::DeviceId, desc: &binding_model::PipelineLayoutDescriptor, - bgl_guard: &Storage, id::BindGroupLayoutId>, - ) -> Result, binding_model::CreatePipelineLayoutError> { + bgl_guard: &Storage, id::BindGroupLayoutId>, + ) -> Result, binding_model::CreatePipelineLayoutError> { use crate::binding_model::CreatePipelineLayoutError as Error; let bind_group_layouts_count = desc.bind_group_layouts.len(); @@ -1883,25 +1600,21 @@ impl Device { .validate(&self.limits) .map_err(Error::TooManyBindings)?; - let descriptor_set_layouts = desc + let bgl_vec = desc .bind_group_layouts .iter() - .map(|&id| &bgl_guard.get(id).unwrap().raw); - let push_constants = desc - .push_constant_ranges - .iter() - .map(|pc| (conv::map_shader_stage_flags(pc.stages), pc.range.clone())); + .map(|&id| &bgl_guard.get(id).unwrap().raw) + .collect::>(); + let hal_desc = hal::PipelineLayoutDescriptor { + label: desc.label.borrow_option(), + bind_group_layouts: &bgl_vec, + push_constant_ranges: desc.push_constant_ranges.as_ref(), + }; let raw = unsafe { - let raw_layout = self - .raw - .create_pipeline_layout(descriptor_set_layouts, push_constants) - .or(Err(DeviceError::OutOfMemory))?; - if let Some(_) = desc.label { - //TODO-0.6: needs gfx changes published - //self.raw.set_pipeline_layout_name(&mut raw_layout, label); - } - raw_layout + self.raw + .create_pipeline_layout(&hal_desc) + .map_err(DeviceError::from)? }; Ok(binding_model::PipelineLayout { @@ -1929,9 +1642,9 @@ impl Device { &self, self_id: id::DeviceId, implicit_context: Option, - mut derived_group_layouts: ArrayVec<[binding_model::BindEntryMap; MAX_BIND_GROUPS]>, - bgl_guard: &mut Storage, id::BindGroupLayoutId>, - pipeline_layout_guard: &mut Storage, id::PipelineLayoutId>, + mut derived_group_layouts: ArrayVec<[binding_model::BindEntryMap; hal::MAX_BIND_GROUPS]>, + bgl_guard: &mut Storage, id::BindGroupLayoutId>, + pipeline_layout_guard: &mut Storage, id::PipelineLayoutId>, ) -> Result { while derived_group_layouts .last() @@ -1977,9 +1690,9 @@ impl Device { self_id: id::DeviceId, desc: &pipeline::ComputePipelineDescriptor, implicit_context: Option, - hub: &Hub, + hub: &Hub, token: &mut Token, - ) -> Result, pipeline::CreateComputePipelineError> { + ) -> Result, pipeline::CreateComputePipelineError> { //TODO: only lock mutable if the layout is derived let (mut pipeline_layout_guard, mut token) = hub.pipeline_layouts.write(token); let (mut bgl_guard, mut token) = hub.bind_group_layouts.write(&mut token); @@ -2002,18 +1715,17 @@ impl Device { } let mut derived_group_layouts = - ArrayVec::<[binding_model::BindEntryMap; MAX_BIND_GROUPS]>::new(); + ArrayVec::<[binding_model::BindEntryMap; hal::MAX_BIND_GROUPS]>::new(); let io = validation::StageIo::default(); let (shader_module_guard, _) = hub.shader_modules.read(&mut token); - let entry_point_name = &desc.stage.entry_point; let shader_module = shader_module_guard .get(desc.stage.module) .map_err(|_| validation::StageError::InvalidModule)?; - let flag = wgt::ShaderStage::COMPUTE; - if let Some(ref interface) = shader_module.interface { + { + let flag = wgt::ShaderStage::COMPUTE; let provided_layouts = match desc.layout { Some(pipeline_layout_id) => Some(Device::get_introspection_bind_group_layouts( pipeline_layout_guard @@ -2028,28 +1740,15 @@ impl Device { None } }; - let _ = interface.check_stage( + let _ = shader_module.interface.check_stage( provided_layouts.as_ref().map(|p| p.as_slice()), &mut derived_group_layouts, - &entry_point_name, + &desc.stage.entry_point, flag, io, )?; - } else if desc.layout.is_none() { - return Err(pipeline::ImplicitLayoutError::ReflectionError(flag).into()); } - let shader = hal::pso::EntryPoint:: { - entry: &entry_point_name, // TODO - module: &shader_module.raw, - specialization: hal::pso::Specialization::EMPTY, - }; - - // TODO - let flags = hal::pso::PipelineCreationFlags::empty(); - // TODO - let parent = hal::pso::BasePipeline::None; - let pipeline_layout_id = match desc.layout { Some(id) => id, None => self.derive_pipeline_layout( @@ -2064,29 +1763,29 @@ impl Device { .get(pipeline_layout_id) .map_err(|_| pipeline::CreateComputePipelineError::InvalidLayout)?; - let pipeline_desc = hal::pso::ComputePipelineDesc { - label: desc.label.as_ref().map(AsRef::as_ref), - shader, + let pipeline_desc = hal::ComputePipelineDescriptor { + label: desc.label.borrow_option(), layout: &layout.raw, - flags, - parent, + stage: hal::ProgrammableStage { + entry_point: desc.stage.entry_point.as_ref(), + module: &shader_module.raw, + }, }; let raw = - unsafe { self.raw.create_compute_pipeline(&pipeline_desc, None) }.map_err(|err| { - match err { - hal::pso::CreationError::OutOfMemory(_) => { - pipeline::CreateComputePipelineError::Device(DeviceError::OutOfMemory) + unsafe { self.raw.create_compute_pipeline(&pipeline_desc) }.map_err( + |err| match err { + hal::PipelineError::Device(error) => { + pipeline::CreateComputePipelineError::Device(error.into()) } - hal::pso::CreationError::ShaderCreationError(_, error) => { - pipeline::CreateComputePipelineError::Internal(error) + hal::PipelineError::Linkage(_stages, msg) => { + pipeline::CreateComputePipelineError::Internal(msg) } - _ => { - log::error!("failed to create compute pipeline: {}", err); - pipeline::CreateComputePipelineError::Device(DeviceError::OutOfMemory) + hal::PipelineError::EntryPoint(_stage) => { + pipeline::CreateComputePipelineError::Internal(EP_FAILURE.to_string()) } - } - })?; + }, + )?; let pipeline = pipeline::ComputePipeline { raw, @@ -2108,9 +1807,9 @@ impl Device { self_id: id::DeviceId, desc: &pipeline::RenderPipelineDescriptor, implicit_context: Option, - hub: &Hub, + hub: &Hub, token: &mut Token, - ) -> Result, pipeline::CreateRenderPipelineError> { + ) -> Result, pipeline::CreateRenderPipelineError> { //TODO: only lock mutable if the layout is derived let (mut pipeline_layout_guard, mut token) = hub.pipeline_layouts.write(token); let (mut bgl_guard, mut token) = hub.bind_group_layouts.write(&mut token); @@ -2125,24 +1824,21 @@ impl Device { } let mut derived_group_layouts = - ArrayVec::<[binding_model::BindEntryMap; MAX_BIND_GROUPS]>::new(); + ArrayVec::<[binding_model::BindEntryMap; hal::MAX_BIND_GROUPS]>::new(); - let color_states = desc + let color_targets = desc .fragment .as_ref() .map_or(&[][..], |fragment| &fragment.targets); let depth_stencil_state = desc.depth_stencil.as_ref(); - let rasterizer = - conv::map_primitive_state_to_rasterizer(&desc.primitive, depth_stencil_state); let mut io = validation::StageIo::default(); let mut validated_stages = wgt::ShaderStage::empty(); - let desc_vbs = &desc.vertex.buffers; - let mut vertex_strides = Vec::with_capacity(desc_vbs.len()); - let mut vertex_buffers = Vec::with_capacity(desc_vbs.len()); - let mut attributes = Vec::new(); - for (i, vb_state) in desc_vbs.iter().enumerate() { + let mut vertex_strides = Vec::with_capacity(desc.vertex.buffers.len()); + let mut vertex_buffers = Vec::with_capacity(desc.vertex.buffers.len()); + let mut total_attributes = 0; + for (i, vb_state) in desc.vertex.buffers.iter().enumerate() { vertex_strides .alloc() .init((vb_state.array_stride, vb_state.step_mode)); @@ -2162,16 +1858,13 @@ impl Device { stride: vb_state.array_stride, }); } - vertex_buffers.alloc().init(hal::pso::VertexBufferDesc { - binding: i as u32, - stride: vb_state.array_stride as u32, - rate: match vb_state.step_mode { - InputStepMode::Vertex => hal::pso::VertexInputRate::Vertex, - InputStepMode::Instance => hal::pso::VertexInputRate::Instance(1), - }, + vertex_buffers.alloc().init(hal::VertexBufferLayout { + array_stride: vb_state.array_stride, + step_mode: vb_state.step_mode, + attributes: vb_state.attributes.as_ref(), }); - let desc_atts = &vb_state.attributes; - for attribute in desc_atts.iter() { + + for attribute in vb_state.attributes.iter() { if attribute.offset >= 0x10000000 { return Err( pipeline::CreateRenderPipelineError::InvalidVertexAttributeOffset { @@ -2189,19 +1882,12 @@ impl Device { self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?; } - attributes.alloc().init(hal::pso::AttributeDesc { - location: attribute.shader_location, - binding: i as u32, - element: hal::pso::Element { - format: conv::map_vertex_format(attribute.format), - offset: attribute.offset as u32, - }, - }); io.insert( attribute.shader_location, validation::InterfaceVar::vertex_attribute(attribute.format), ); } + total_attributes += vb_state.attributes.len(); } if vertex_buffers.len() > self.limits.max_vertex_buffers as usize { @@ -2210,19 +1896,16 @@ impl Device { limit: self.limits.max_vertex_buffers, }); } - if attributes.len() > self.limits.max_vertex_attributes as usize { + if total_attributes > self.limits.max_vertex_attributes as usize { return Err( pipeline::CreateRenderPipelineError::TooManyVertexAttributes { - given: attributes.len() as u32, + given: total_attributes as u32, limit: self.limits.max_vertex_attributes, }, ); } - if desc.primitive.strip_index_format.is_some() - && desc.primitive.topology != wgt::PrimitiveTopology::LineStrip - && desc.primitive.topology != wgt::PrimitiveTopology::TriangleStrip - { + if desc.primitive.strip_index_format.is_some() && !desc.primitive.topology.is_strip() { return Err( pipeline::CreateRenderPipelineError::StripIndexFormatForNonStripTopology { strip_index_format: desc.primitive.strip_index_format, @@ -2248,13 +1931,7 @@ impl Device { ); } - let input_assembler = conv::map_primitive_state_to_input_assembler(&desc.primitive); - - let mut blender = hal::pso::BlendDesc { - logic_op: None, - targets: Vec::with_capacity(color_states.len()), - }; - for (i, cs) in color_states.iter().enumerate() { + for (i, cs) in color_targets.iter().enumerate() { let error = loop { let format_desc = cs.format.describe(); self.require_features(format_desc.required_features)?; @@ -2268,19 +1945,10 @@ impl Device { if cs.blend.is_some() && !format_desc.guaranteed_format_features.filterable { break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format)); } - let hal_format = conv::map_texture_format(cs.format, self.private_features); - if !hal_format - .surface_desc() - .aspects - .contains(hal::format::Aspects::COLOR) - { + if !hal::FormatAspect::from(cs.format).contains(hal::FormatAspect::COLOR) { break Some(pipeline::ColorStateError::FormatNotColor(cs.format)); } - match conv::map_color_target_state(cs) { - Ok(bt) => blender.targets.push(bt), - Err(e) => break Some(e), - } break None; }; if let Some(e) = error { @@ -2301,12 +1969,11 @@ impl Device { ds.format, )); } - let hal_format = conv::map_texture_format(ds.format, self.private_features); - let aspects = hal_format.surface_desc().aspects; - if ds.is_depth_enabled() && !aspects.contains(hal::format::Aspects::DEPTH) { + let aspect = hal::FormatAspect::from(ds.format); + if ds.is_depth_enabled() && !aspect.contains(hal::FormatAspect::DEPTH) { break Some(pipeline::DepthStencilStateError::FormatNotDepth(ds.format)); } - if ds.stencil.is_enabled() && !aspects.contains(hal::format::Aspects::STENCIL) { + if ds.stencil.is_enabled() && !aspect.contains(hal::FormatAspect::STENCIL) { break Some(pipeline::DepthStencilStateError::FormatNotStencil( ds.format, )); @@ -2317,16 +1984,6 @@ impl Device { return Err(pipeline::CreateRenderPipelineError::DepthStencilState(e)); } } - let depth_stencil = depth_stencil_state - .map(conv::map_depth_stencil_state) - .unwrap_or_default(); - - let baked_states = hal::pso::BakedStates { - viewport: None, - scissor: None, - blend_constants: None, - depth_bounds: None, - }; if desc.layout.is_none() { for _ in 0..self.limits.max_bind_groups { @@ -2339,54 +1996,12 @@ impl Device { if sc == 0 || sc > 32 || !conv::is_power_of_two(sc) { return Err(pipeline::CreateRenderPipelineError::InvalidSampleCount(sc)); } - sc as u8 - }; - let multisampling = if samples == 1 { - None - } else { - Some(conv::map_multisample_state(&desc.multisample)) - }; - - let rp_key = RenderPassKey { - colors: color_states - .iter() - .map(|state| { - let at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - state.format, - self.private_features, - )), - samples, - ops: hal::pass::AttachmentOps::PRESERVE, - stencil_ops: hal::pass::AttachmentOps::DONT_CARE, - layouts: hal::image::Layout::General..hal::image::Layout::General, - }; - (at, hal::image::Layout::ColorAttachmentOptimal) - }) - .collect(), - // We can ignore the resolves as the vulkan specs says: - // As an additional special case, if two render passes have a single subpass, - // they are compatible even if they have different resolve attachment references - // or depth/stencil resolve modes but satisfy the other compatibility conditions. - resolves: ArrayVec::new(), - depth_stencil: depth_stencil_state.map(|state| { - let at = hal::pass::Attachment { - format: Some(conv::map_texture_format( - state.format, - self.private_features, - )), - samples, - ops: hal::pass::AttachmentOps::PRESERVE, - stencil_ops: hal::pass::AttachmentOps::PRESERVE, - layouts: hal::image::Layout::General..hal::image::Layout::General, - }; - (at, hal::image::Layout::DepthStencilAttachmentOptimal) - }), + sc }; let (shader_module_guard, _) = hub.shader_modules.read(&mut token); - let vertex = { + let vertex_stage = { let stage = &desc.vertex.stage; let flag = wgt::ShaderStage::VERTEX; @@ -2397,45 +2012,42 @@ impl Device { } })?; - if let Some(ref interface) = shader_module.interface { - let provided_layouts = match desc.layout { - Some(pipeline_layout_id) => { - let pipeline_layout = pipeline_layout_guard - .get(pipeline_layout_id) - .map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?; - Some(Device::get_introspection_bind_group_layouts( - pipeline_layout, - &*bgl_guard, - )) - } - None => None, - }; + let provided_layouts = match desc.layout { + Some(pipeline_layout_id) => { + let pipeline_layout = pipeline_layout_guard + .get(pipeline_layout_id) + .map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?; + Some(Device::get_introspection_bind_group_layouts( + pipeline_layout, + &*bgl_guard, + )) + } + None => None, + }; - io = interface - .check_stage( - provided_layouts.as_ref().map(|p| p.as_slice()), - &mut derived_group_layouts, - &stage.entry_point, - flag, - io, - ) - .map_err(|error| pipeline::CreateRenderPipelineError::Stage { - stage: flag, - error, - })?; - validated_stages |= flag; - } + io = shader_module + .interface + .check_stage( + provided_layouts.as_ref().map(|p| p.as_slice()), + &mut derived_group_layouts, + &stage.entry_point, + flag, + io, + ) + .map_err(|error| pipeline::CreateRenderPipelineError::Stage { + stage: flag, + error, + })?; + validated_stages |= flag; - hal::pso::EntryPoint:: { - entry: &stage.entry_point, + hal::ProgrammableStage { module: &shader_module.raw, - specialization: hal::pso::Specialization::EMPTY, + entry_point: stage.entry_point.as_ref(), } }; - let fragment = match desc.fragment { + let fragment_stage = match desc.fragment { Some(ref fragment) => { - let entry_point_name = &fragment.stage.entry_point; let flag = wgt::ShaderStage::FRAGMENT; let shader_module = @@ -2457,34 +2069,32 @@ impl Device { }; if validated_stages == wgt::ShaderStage::VERTEX { - if let Some(ref interface) = shader_module.interface { - io = interface - .check_stage( - provided_layouts.as_ref().map(|p| p.as_slice()), - &mut derived_group_layouts, - &entry_point_name, - flag, - io, - ) - .map_err(|error| pipeline::CreateRenderPipelineError::Stage { - stage: flag, - error, - })?; - validated_stages |= flag; - } + io = shader_module + .interface + .check_stage( + provided_layouts.as_ref().map(|p| p.as_slice()), + &mut derived_group_layouts, + &fragment.stage.entry_point, + flag, + io, + ) + .map_err(|error| pipeline::CreateRenderPipelineError::Stage { + stage: flag, + error, + })?; + validated_stages |= flag; } - Some(hal::pso::EntryPoint:: { - entry: &entry_point_name, + Some(hal::ProgrammableStage { module: &shader_module.raw, - specialization: hal::pso::Specialization::EMPTY, + entry_point: fragment.stage.entry_point.as_ref(), }) } None => None, }; if validated_stages.contains(wgt::ShaderStage::FRAGMENT) { - for (i, state) in color_states.iter().enumerate() { + for (i, state) in color_targets.iter().enumerate() { match io.get(&(i as wgt::ShaderLocation)) { Some(ref output) => { validation::check_texture_format(state.format, &output.ty).map_err( @@ -2518,20 +2128,6 @@ impl Device { return Err(pipeline::ImplicitLayoutError::ReflectionError(last_stage).into()); } - let primitive_assembler = hal::pso::PrimitiveAssemblerDesc::Vertex { - buffers: &vertex_buffers, - attributes: &attributes, - input_assembler, - vertex, - tessellation: None, - geometry: None, - }; - - // TODO - let flags = hal::pso::PipelineCreationFlags::empty(); - // TODO - let parent = hal::pso::BasePipeline::None; - let pipeline_layout_id = match desc.layout { Some(id) => id, None => self.derive_pipeline_layout( @@ -2546,55 +2142,38 @@ impl Device { .get(pipeline_layout_id) .map_err(|_| pipeline::CreateRenderPipelineError::InvalidLayout)?; - let mut rp_lock = self.render_passes.lock(); - let pipeline_desc = hal::pso::GraphicsPipelineDesc { - label: desc.label.as_ref().map(AsRef::as_ref), - primitive_assembler, - rasterizer, - fragment, - blender, - depth_stencil, - multisampling, - baked_states, + let pipeline_desc = hal::RenderPipelineDescriptor { + label: desc.label.borrow_option(), layout: &layout.raw, - subpass: hal::pass::Subpass { - index: 0, - main_pass: match rp_lock.render_passes.entry(rp_key) { - Entry::Occupied(e) => e.into_mut(), - Entry::Vacant(e) => { - let pass = self - .create_compatible_render_pass(e.key()) - .or(Err(DeviceError::OutOfMemory))?; - e.insert(pass) - } - }, - }, - flags, - parent, + vertex_buffers: &vertex_buffers, + vertex_stage, + primitive: desc.primitive, + depth_stencil: desc.depth_stencil.clone(), + multisample: desc.multisample, + fragment_stage, + color_targets, }; - // TODO: cache let raw = - unsafe { self.raw.create_graphics_pipeline(&pipeline_desc, None) }.map_err(|err| { - match err { - hal::pso::CreationError::OutOfMemory(_) => { - pipeline::CreateRenderPipelineError::Device(DeviceError::OutOfMemory) + unsafe { self.raw.create_render_pipeline(&pipeline_desc) }.map_err( + |err| match err { + hal::PipelineError::Device(error) => { + pipeline::CreateRenderPipelineError::Device(error.into()) + } + hal::PipelineError::Linkage(stage, msg) => { + pipeline::CreateRenderPipelineError::Internal { stage, error: msg } } - hal::pso::CreationError::ShaderCreationError(stage, error) => { + hal::PipelineError::EntryPoint(stage) => { pipeline::CreateRenderPipelineError::Internal { - stage: conv::map_hal_flags_to_shader_stage(stage), - error, + stage: hal::util::map_naga_stage(stage), + error: EP_FAILURE.to_string(), } } - _ => { - log::error!("failed to create graphics pipeline: {}", err); - pipeline::CreateRenderPipelineError::Device(DeviceError::OutOfMemory) - } - } - })?; + }, + )?; let pass_context = RenderPassContext { attachments: AttachmentData { - colors: color_states.iter().map(|state| state.format).collect(), + colors: color_targets.iter().map(|state| state.format).collect(), resolves: ArrayVec::new(), depth_stencil: depth_stencil_state.as_ref().map(|state| state.format), }, @@ -2602,7 +2181,7 @@ impl Device { }; let mut flags = pipeline::PipelineFlags::empty(); - for state in color_states.iter() { + for state in color_targets.iter() { if let Some(ref bs) = state.blend { if bs.color.uses_constant() | bs.alpha.uses_constant() { flags |= pipeline::PipelineFlags::BLEND_CONSTANT; @@ -2642,24 +2221,33 @@ impl Device { submission_index: SubmissionIndex, token: &mut Token, ) -> Result<(), WaitIdleError> { - if self.last_completed_submission_index() <= submission_index { + let last_done_index = unsafe { + self.raw + .get_fence_value(&self.fence) + .map_err(DeviceError::from)? + }; + if last_done_index < submission_index { log::info!("Waiting for submission {:?}", submission_index); + unsafe { + self.raw + .wait(&self.fence, submission_index, !0) + .map_err(DeviceError::from)? + }; self.lock_life(token) - .triage_submissions(&self.raw, true) - .map(|_| ()) - } else { - Ok(()) + .triage_submissions(submission_index, &self.command_allocator); } + Ok(()) } fn create_query_set( &self, self_id: id::DeviceId, - desc: &wgt::QuerySetDescriptor, - ) -> Result, resource::CreateQuerySetError> { + desc: &resource::QuerySetDescriptor, + ) -> Result, resource::CreateQuerySetError> { use resource::CreateQuerySetError as Error; match desc.ty { + wgt::QueryType::Occlusion => {} wgt::QueryType::Timestamp => { self.require_features(wgt::Features::TIMESTAMP_QUERY)?; } @@ -2679,76 +2267,68 @@ impl Device { }); } - let (hal_type, elements) = conv::map_query_type(&desc.ty); - + let hal_desc = desc.map_label(super::LabelHelpers::borrow_option); Ok(resource::QuerySet { - raw: unsafe { self.raw.create_query_pool(hal_type, desc.count).unwrap() }, + raw: unsafe { self.raw.create_query_set(&hal_desc).unwrap() }, device_id: Stored { value: id::Valid(self_id), ref_count: self.life_guard.add_ref(), }, life_guard: LifeGuard::new(""), - desc: desc.clone(), - elements, + desc: desc.map_label(|_| ()), }) } } -impl Device { - pub(crate) fn destroy_bind_group(&self, bind_group: binding_model::BindGroup) { - self.desc_allocator - .lock() - .free(&self.raw, iter::once(bind_group.raw)); - } - - pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer) { - if let Some((raw, memory)) = buffer.raw { +impl Device { + pub(crate) fn destroy_buffer(&self, buffer: resource::Buffer) { + if let Some(raw) = buffer.raw { unsafe { - self.mem_allocator.lock().free(&self.raw, memory); self.raw.destroy_buffer(raw); } } } - pub(crate) fn destroy_texture(&self, texture: resource::Texture) { - if let Some((raw, memory)) = texture.raw { + pub(crate) fn destroy_texture(&self, texture: resource::Texture) { + if let Some(raw) = texture.raw { unsafe { - self.mem_allocator.lock().free(&self.raw, memory); - self.raw.destroy_image(raw); + self.raw.destroy_texture(raw); } } } + pub(crate) fn destroy_command_buffer(&self, cmd_buf: command::CommandBuffer) { + let mut baked = cmd_buf.into_baked(); + unsafe { + baked.encoder.reset_all(baked.list.into_iter()); + } + unsafe { + self.raw.destroy_command_encoder(baked.encoder); + } + } + /// Wait for idle and remove resources that we can, before we die. pub(crate) fn prepare_to_die(&mut self) { let mut life_tracker = self.life_tracker.lock(); - if let Err(error) = life_tracker.triage_submissions(&self.raw, true) { - log::error!("failed to triage submissions: {}", error); + let current_index = self.active_submission_index; + if let Err(error) = unsafe { self.raw.wait(&self.fence, current_index, CLEANUP_WAIT_MS) } { + log::error!("failed to wait for the device: {:?}", error); } - life_tracker.cleanup(&self.raw, &self.mem_allocator, &self.desc_allocator); + life_tracker.triage_submissions(current_index, &self.command_allocator); + life_tracker.cleanup(&self.raw); } pub(crate) fn dispose(self) { - let mut desc_alloc = self.desc_allocator.into_inner(); - let mut mem_alloc = self.mem_allocator.into_inner(); - self.pending_writes - .dispose(&self.raw, &self.cmd_allocator, &mut mem_alloc); - self.cmd_allocator.destroy(&self.raw); + self.pending_writes.dispose(&self.raw); + self.command_allocator.into_inner().dispose(&self.raw); unsafe { - desc_alloc.cleanup(&self.raw); - mem_alloc.clear(&self.raw); - let rps = self.render_passes.into_inner(); - for (_, rp) in rps.render_passes { - self.raw.destroy_render_pass(rp); - } - for (_, fbo) in rps.framebuffers { - self.raw.destroy_framebuffer(fbo); - } + self.raw.destroy_fence(self.fence); + self.raw.exit(); } } } -impl crate::hub::Resource for Device { +impl crate::hub::Resource for Device { const TYPE: &'static str = "Device"; fn life_guard(&self) -> &LifeGuard { @@ -2770,31 +2350,11 @@ pub enum DeviceError { OutOfMemory, } -impl From for DeviceError { - fn from(err: hal::device::WaitError) -> Self { - match err { - hal::device::WaitError::OutOfMemory(_) => Self::OutOfMemory, - hal::device::WaitError::DeviceLost(_) => Self::Lost, - } - } -} - -impl From for DeviceError { - fn from(err: gpu_alloc::MapError) -> Self { - match err { - gpu_alloc::MapError::OutOfDeviceMemory | gpu_alloc::MapError::OutOfHostMemory => { - DeviceError::OutOfMemory - } - _ => panic!("failed to map buffer: {}", err), - } - } -} - -impl DeviceError { - fn from_bind(err: hal::device::BindError) -> Self { - match err { - hal::device::BindError::OutOfMemory(_) => Self::OutOfMemory, - _ => panic!("failed to bind memory: {}", err), +impl From for DeviceError { + fn from(error: hal::DeviceError) -> Self { + match error { + hal::DeviceError::Lost => DeviceError::Lost, + hal::DeviceError::OutOfMemory => DeviceError::OutOfMemory, } } } @@ -2808,7 +2368,7 @@ pub struct MissingFeatures(pub wgt::Features); #[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub struct ImplicitPipelineContext { pub root_id: id::PipelineLayoutId, - pub group_ids: ArrayVec<[id::BindGroupLayoutId; MAX_BIND_GROUPS]>, + pub group_ids: ArrayVec<[id::BindGroupLayoutId; hal::MAX_BIND_GROUPS]>, } pub struct ImplicitPipelineIds<'a, G: GlobalIdentityHandlerFactory> { @@ -2817,7 +2377,7 @@ pub struct ImplicitPipelineIds<'a, G: GlobalIdentityHandlerFactory> { } impl ImplicitPipelineIds<'_, G> { - fn prepare(self, hub: &Hub) -> ImplicitPipelineContext { + fn prepare(self, hub: &Hub) -> ImplicitPipelineContext { ImplicitPipelineContext { root_id: hub.pipeline_layouts.prepare(self.root_id).into_id(), group_ids: self @@ -2830,12 +2390,12 @@ impl ImplicitPipelineIds<'_, G> { } impl Global { - pub fn adapter_get_swap_chain_preferred_format( + pub fn adapter_get_swap_chain_preferred_format( &self, adapter_id: id::AdapterId, surface_id: id::SurfaceId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut surface_guard, mut token) = self.surfaces.write(&mut token); @@ -2850,11 +2410,11 @@ impl Global { adapter.get_swap_chain_preferred_format(surface) } - pub fn device_features( + pub fn device_features( &self, device_id: id::DeviceId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, _) = hub.devices.read(&mut token); let device = device_guard.get(device_id).map_err(|_| InvalidDevice)?; @@ -2862,11 +2422,11 @@ impl Global { Ok(device.features) } - pub fn device_limits( + pub fn device_limits( &self, device_id: id::DeviceId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, _) = hub.devices.read(&mut token); let device = device_guard.get(device_id).map_err(|_| InvalidDevice)?; @@ -2874,11 +2434,11 @@ impl Global { Ok(device.limits.clone()) } - pub fn device_downlevel_properties( + pub fn device_downlevel_properties( &self, device_id: id::DeviceId, - ) -> Result { - let hub = B::hub(self); + ) -> Result { + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, _) = hub.devices.read(&mut token); let device = device_guard.get(device_id).map_err(|_| InvalidDevice)?; @@ -2886,7 +2446,7 @@ impl Global { Ok(device.downlevel) } - pub fn device_create_buffer( + pub fn device_create_buffer( &self, device_id: id::DeviceId, desc: &resource::BufferDescriptor, @@ -2894,7 +2454,7 @@ impl Global { ) -> (id::BufferId, Option) { profiling::scope!("create_buffer", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.buffers.prepare(id_in); @@ -2923,28 +2483,26 @@ impl Global { let ref_count = buffer.life_guard.add_ref(); let buffer_use = if !desc.mapped_at_creation { - resource::BufferUse::EMPTY + hal::BufferUse::empty() } else if desc.usage.contains(wgt::BufferUsage::MAP_WRITE) { // buffer is mappable, so we are just doing that at start let map_size = buffer.size; let ptr = match map_buffer(&device.raw, &mut buffer, 0, map_size, HostMap::Write) { Ok(ptr) => ptr, Err(e) => { - let (raw, memory) = buffer.raw.unwrap(); - device.lock_life(&mut token).schedule_resource_destruction( - queue::TempResource::Buffer(raw), - memory, - !0, - ); + let raw = buffer.raw.unwrap(); + device + .lock_life(&mut token) + .schedule_resource_destruction(queue::TempResource::Buffer(raw), !0); break e.into(); } }; buffer.map_state = resource::BufferMapState::Active { ptr, - sub_range: hal::buffer::SubRange::WHOLE, + range: 0..map_size, host: HostMap::Write, }; - resource::BufferUse::MAP_WRITE + hal::BufferUse::MAP_WRITE } else { // buffer needs staging area for initialization only let stage_desc = wgt::BufferDescriptor { @@ -2956,48 +2514,41 @@ impl Global { let mut stage = match device.create_buffer(device_id, &stage_desc, true) { Ok(stage) => stage, Err(e) => { - let (raw, memory) = buffer.raw.unwrap(); - device.lock_life(&mut token).schedule_resource_destruction( - queue::TempResource::Buffer(raw), - memory, - !0, - ); + let raw = buffer.raw.unwrap(); + device + .lock_life(&mut token) + .schedule_resource_destruction(queue::TempResource::Buffer(raw), !0); break e; } }; - let (stage_buffer, mut stage_memory) = stage.raw.unwrap(); - let ptr = match stage_memory.map(&device.raw, 0, stage.size) { - Ok(ptr) => ptr, + let stage_buffer = stage.raw.unwrap(); + let mapping = match unsafe { device.raw.map_buffer(&stage_buffer, 0..stage.size) } { + Ok(mapping) => mapping, Err(e) => { - let (raw, memory) = buffer.raw.unwrap(); + let raw = buffer.raw.unwrap(); let mut life_lock = device.lock_life(&mut token); - life_lock.schedule_resource_destruction( - queue::TempResource::Buffer(raw), - memory, - !0, - ); + life_lock + .schedule_resource_destruction(queue::TempResource::Buffer(raw), !0); life_lock.schedule_resource_destruction( queue::TempResource::Buffer(stage_buffer), - stage_memory, !0, ); - break e.into(); + break DeviceError::from(e).into(); } }; // Zero initialize memory and then mark both staging and buffer as initialized // (it's guaranteed that this is the case by the time the buffer is usable) - unsafe { ptr::write_bytes(ptr.as_ptr(), 0, buffer.size as usize) }; + unsafe { ptr::write_bytes(mapping.ptr.as_ptr(), 0, buffer.size as usize) }; buffer.initialization_status.clear(0..buffer.size); stage.initialization_status.clear(0..buffer.size); buffer.map_state = resource::BufferMapState::Init { - ptr, - needs_flush: !stage_memory.is_coherent(), + ptr: mapping.ptr, + needs_flush: !mapping.is_coherent, stage_buffer, - stage_memory, }; - resource::BufferUse::COPY_DST + hal::BufferUse::COPY_DST }; let id = fid.assign(buffer, &mut token); @@ -3017,12 +2568,12 @@ impl Global { } #[cfg(feature = "replay")] - pub fn device_wait_for_buffer( + pub fn device_wait_for_buffer( &self, device_id: id::DeviceId, buffer_id: id::BufferId, ) -> Result<(), WaitIdleError> { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); let last_submission = { @@ -3039,7 +2590,8 @@ impl Global { .wait_for_submit(last_submission, &mut token) } - pub fn device_set_buffer_sub_data( + #[doc(hidden)] + pub fn device_set_buffer_sub_data( &self, device_id: id::DeviceId, buffer_id: id::BufferId, @@ -3048,7 +2600,7 @@ impl Global { ) -> Result<(), resource::BufferAccessError> { profiling::scope!("set_buffer_sub_data", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -3074,17 +2626,29 @@ impl Global { }); } - buffer - .raw - .as_mut() - .unwrap() - .1 - .write_bytes(&device.raw, offset, data)?; + let raw_buf = buffer.raw.as_ref().unwrap(); + unsafe { + let mapping = device + .raw + .map_buffer(raw_buf, offset..offset + data.len() as u64) + .map_err(DeviceError::from)?; + ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()); + if !mapping.is_coherent { + device + .raw + .flush_mapped_ranges(raw_buf, iter::once(offset..offset + data.len() as u64)); + } + device + .raw + .unmap_buffer(raw_buf) + .map_err(DeviceError::from)?; + } Ok(()) } - pub fn device_get_buffer_sub_data( + #[doc(hidden)] + pub fn device_get_buffer_sub_data( &self, device_id: id::DeviceId, buffer_id: id::BufferId, @@ -3093,7 +2657,7 @@ impl Global { ) -> Result<(), resource::BufferAccessError> { profiling::scope!("get_buffer_sub_data", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -3107,27 +2671,39 @@ impl Global { check_buffer_usage(buffer.usage, wgt::BufferUsage::MAP_READ)?; //assert!(buffer isn't used by the GPU); - buffer - .raw - .as_mut() - .unwrap() - .1 - .read_bytes(&device.raw, offset, data)?; + let raw_buf = buffer.raw.as_ref().unwrap(); + unsafe { + let mapping = device + .raw + .map_buffer(raw_buf, offset..offset + data.len() as u64) + .map_err(DeviceError::from)?; + if !mapping.is_coherent { + device.raw.invalidate_mapped_ranges( + raw_buf, + iter::once(offset..offset + data.len() as u64), + ); + } + ptr::copy_nonoverlapping(mapping.ptr.as_ptr(), data.as_mut_ptr(), data.len()); + device + .raw + .unmap_buffer(raw_buf) + .map_err(DeviceError::from)?; + } Ok(()) } - pub fn buffer_label(&self, id: id::BufferId) -> String { - B::hub(self).buffers.label_for_resource(id) + pub fn buffer_label(&self, id: id::BufferId) -> String { + A::hub(self).buffers.label_for_resource(id) } - pub fn buffer_destroy( + pub fn buffer_destroy( &self, buffer_id: id::BufferId, ) -> Result<(), resource::DestroyError> { profiling::scope!("destroy", "Buffer"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); //TODO: lock pending writes separately, keep the device read-only @@ -3146,31 +2722,29 @@ impl Global { trace.lock().add(trace::Action::FreeBuffer(buffer_id)); } - let (raw, memory) = buffer + let raw = buffer .raw .take() .ok_or(resource::DestroyError::AlreadyDestroyed)?; let temp = queue::TempResource::Buffer(raw); if device.pending_writes.dst_buffers.contains(&buffer_id) { - device.pending_writes.temp_resources.push((temp, memory)); + device.pending_writes.temp_resources.push(temp); } else { let last_submit_index = buffer.life_guard.submission_index.load(Ordering::Acquire); drop(buffer_guard); - device.lock_life(&mut token).schedule_resource_destruction( - temp, - memory, - last_submit_index, - ); + device + .lock_life(&mut token) + .schedule_resource_destruction(temp, last_submit_index); } Ok(()) } - pub fn buffer_drop(&self, buffer_id: id::BufferId, wait: bool) { + pub fn buffer_drop(&self, buffer_id: id::BufferId, wait: bool) { profiling::scope!("drop", "Buffer"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); log::info!("Buffer {:?} is dropped", buffer_id); @@ -3216,7 +2790,7 @@ impl Global { } } - pub fn device_create_texture( + pub fn device_create_texture( &self, device_id: id::DeviceId, desc: &resource::TextureDescriptor, @@ -3224,7 +2798,7 @@ impl Global { ) -> (id::TextureId, Option) { profiling::scope!("create_texture", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.textures.prepare(id_in); @@ -3267,17 +2841,17 @@ impl Global { (id, Some(error)) } - pub fn texture_label(&self, id: id::TextureId) -> String { - B::hub(self).textures.label_for_resource(id) + pub fn texture_label(&self, id: id::TextureId) -> String { + A::hub(self).textures.label_for_resource(id) } - pub fn texture_destroy( + pub fn texture_destroy( &self, texture_id: id::TextureId, ) -> Result<(), resource::DestroyError> { profiling::scope!("destroy", "Texture"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); //TODO: lock pending writes separately, keep the device read-only @@ -3296,31 +2870,29 @@ impl Global { trace.lock().add(trace::Action::FreeTexture(texture_id)); } - let (raw, memory) = texture + let raw = texture .raw .take() .ok_or(resource::DestroyError::AlreadyDestroyed)?; - let temp = queue::TempResource::Image(raw); + let temp = queue::TempResource::Texture(raw); if device.pending_writes.dst_textures.contains(&texture_id) { - device.pending_writes.temp_resources.push((temp, memory)); + device.pending_writes.temp_resources.push(temp); } else { let last_submit_index = texture.life_guard.submission_index.load(Ordering::Acquire); drop(texture_guard); - device.lock_life(&mut token).schedule_resource_destruction( - temp, - memory, - last_submit_index, - ); + device + .lock_life(&mut token) + .schedule_resource_destruction(temp, last_submit_index); } Ok(()) } - pub fn texture_drop(&self, texture_id: id::TextureId, wait: bool) { + pub fn texture_drop(&self, texture_id: id::TextureId, wait: bool) { profiling::scope!("drop", "Texture"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (ref_count, last_submit_index, device_id) = { @@ -3366,7 +2938,7 @@ impl Global { } } - pub fn texture_create_view( + pub fn texture_create_view( &self, texture_id: id::TextureId, desc: &resource::TextureViewDescriptor, @@ -3374,7 +2946,7 @@ impl Global { ) -> (id::TextureViewId, Option) { profiling::scope!("create_view", "Texture"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.texture_views.prepare(id_in); @@ -3415,18 +2987,18 @@ impl Global { (id, Some(error)) } - pub fn texture_view_label(&self, id: id::TextureViewId) -> String { - B::hub(self).texture_views.label_for_resource(id) + pub fn texture_view_label(&self, id: id::TextureViewId) -> String { + A::hub(self).texture_views.label_for_resource(id) } - pub fn texture_view_drop( + pub fn texture_view_drop( &self, texture_view_id: id::TextureViewId, wait: bool, ) -> Result<(), resource::TextureViewDestroyError> { profiling::scope!("drop", "TextureView"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (last_submit_index, device_id) = { @@ -3438,11 +3010,11 @@ impl Global { let _ref_count = view.life_guard.ref_count.take(); let last_submit_index = view.life_guard.submission_index.load(Ordering::Acquire); - let device_id = match view.inner { - resource::TextureViewInner::Native { ref source_id, .. } => { + let device_id = match view.source { + resource::TextureViewSource::Native(ref source_id) => { texture_guard[source_id.value].device_id.value } - resource::TextureViewInner::SwapChain { .. } => { + resource::TextureViewSource::SwapChain(_) => { return Err(resource::TextureViewDestroyError::SwapChainImage) } }; @@ -3477,7 +3049,7 @@ impl Global { Ok(()) } - pub fn device_create_sampler( + pub fn device_create_sampler( &self, device_id: id::DeviceId, desc: &resource::SamplerDescriptor, @@ -3485,7 +3057,7 @@ impl Global { ) -> (id::SamplerId, Option) { profiling::scope!("create_sampler", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.samplers.prepare(id_in); @@ -3522,14 +3094,14 @@ impl Global { (id, Some(error)) } - pub fn sampler_label(&self, id: id::SamplerId) -> String { - B::hub(self).samplers.label_for_resource(id) + pub fn sampler_label(&self, id: id::SamplerId) -> String { + A::hub(self).samplers.label_for_resource(id) } - pub fn sampler_drop(&self, sampler_id: id::SamplerId) { + pub fn sampler_drop(&self, sampler_id: id::SamplerId) { profiling::scope!("drop", "Sampler"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let device_id = { @@ -3555,7 +3127,7 @@ impl Global { .push(id::Valid(sampler_id)); } - pub fn device_create_bind_group_layout( + pub fn device_create_bind_group_layout( &self, device_id: id::DeviceId, desc: &binding_model::BindGroupLayoutDescriptor, @@ -3567,7 +3139,7 @@ impl Global { profiling::scope!("create_bind_group_layout", "Device"); let mut token = Token::root(); - let hub = B::hub(self); + let hub = A::hub(self); let fid = hub.bind_group_layouts.prepare(id_in); let error = 'outer: loop { @@ -3606,7 +3178,7 @@ impl Global { let layout = match device.create_bind_group_layout( device_id, - desc.label.as_ref().map(|cow| cow.as_ref()), + desc.label.borrow_option(), entry_map, ) { Ok(layout) => layout, @@ -3621,17 +3193,14 @@ impl Global { (id, Some(error)) } - pub fn bind_group_layout_label(&self, id: id::BindGroupLayoutId) -> String { - B::hub(self).bind_group_layouts.label_for_resource(id) + pub fn bind_group_layout_label(&self, id: id::BindGroupLayoutId) -> String { + A::hub(self).bind_group_layouts.label_for_resource(id) } - pub fn bind_group_layout_drop( - &self, - bind_group_layout_id: id::BindGroupLayoutId, - ) { + pub fn bind_group_layout_drop(&self, bind_group_layout_id: id::BindGroupLayoutId) { profiling::scope!("drop", "BindGroupLayout"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let device_id = { let (mut bind_group_layout_guard, _) = hub.bind_group_layouts.write(&mut token); @@ -3653,7 +3222,7 @@ impl Global { .push(id::Valid(bind_group_layout_id)); } - pub fn device_create_pipeline_layout( + pub fn device_create_pipeline_layout( &self, device_id: id::DeviceId, desc: &binding_model::PipelineLayoutDescriptor, @@ -3664,7 +3233,7 @@ impl Global { ) { profiling::scope!("create_pipeline_layout", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.pipeline_layouts.prepare(id_in); @@ -3697,14 +3266,14 @@ impl Global { (id, Some(error)) } - pub fn pipeline_layout_label(&self, id: id::PipelineLayoutId) -> String { - B::hub(self).pipeline_layouts.label_for_resource(id) + pub fn pipeline_layout_label(&self, id: id::PipelineLayoutId) -> String { + A::hub(self).pipeline_layouts.label_for_resource(id) } - pub fn pipeline_layout_drop(&self, pipeline_layout_id: id::PipelineLayoutId) { + pub fn pipeline_layout_drop(&self, pipeline_layout_id: id::PipelineLayoutId) { profiling::scope!("drop", "PipelineLayout"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_id, ref_count) = { let (mut pipeline_layout_guard, _) = hub.pipeline_layouts.write(&mut token); @@ -3732,7 +3301,7 @@ impl Global { }); } - pub fn device_create_bind_group( + pub fn device_create_bind_group( &self, device_id: id::DeviceId, desc: &binding_model::BindGroupDescriptor, @@ -3740,7 +3309,7 @@ impl Global { ) -> (id::BindGroupId, Option) { profiling::scope!("create_bind_group", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.bind_groups.prepare(id_in); @@ -3795,14 +3364,14 @@ impl Global { (id, Some(error)) } - pub fn bind_group_label(&self, id: id::BindGroupId) -> String { - B::hub(self).bind_groups.label_for_resource(id) + pub fn bind_group_label(&self, id: id::BindGroupId) -> String { + A::hub(self).bind_groups.label_for_resource(id) } - pub fn bind_group_drop(&self, bind_group_id: id::BindGroupId) { + pub fn bind_group_drop(&self, bind_group_id: id::BindGroupId) { profiling::scope!("drop", "BindGroup"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let device_id = { @@ -3828,7 +3397,7 @@ impl Global { .push(id::Valid(bind_group_id)); } - pub fn device_create_shader_module( + pub fn device_create_shader_module( &self, device_id: id::DeviceId, desc: &pipeline::ShaderModuleDescriptor, @@ -3840,7 +3409,7 @@ impl Global { ) { profiling::scope!("create_shader_module", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.shader_modules.prepare(id_in); @@ -3886,14 +3455,14 @@ impl Global { (id, Some(error)) } - pub fn shader_module_label(&self, id: id::ShaderModuleId) -> String { - B::hub(self).shader_modules.label_for_resource(id) + pub fn shader_module_label(&self, id: id::ShaderModuleId) -> String { + A::hub(self).shader_modules.label_for_resource(id) } - pub fn shader_module_drop(&self, shader_module_id: id::ShaderModuleId) { + pub fn shader_module_drop(&self, shader_module_id: id::ShaderModuleId) { profiling::scope!("drop", "ShaderModule"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); let (module, _) = hub.shader_modules.unregister(shader_module_id, &mut token); @@ -3911,15 +3480,15 @@ impl Global { } } - pub fn device_create_command_encoder( + pub fn device_create_command_encoder( &self, device_id: id::DeviceId, desc: &wgt::CommandEncoderDescriptor(command_buffer_id) } pub fn device_create_render_bundle_encoder( @@ -4008,7 +3572,7 @@ impl Global { (Box::into_raw(Box::new(encoder)), error) } - pub fn render_bundle_encoder_finish( + pub fn render_bundle_encoder_finish( &self, bundle_encoder: command::RenderBundleEncoder, desc: &command::RenderBundleDescriptor, @@ -4016,7 +3580,7 @@ impl Global { ) -> (id::RenderBundleId, Option) { profiling::scope!("finish", "RenderBundleEncoder"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.render_bundles.prepare(id_in); @@ -4060,13 +3624,13 @@ impl Global { (id, Some(error)) } - pub fn render_bundle_label(&self, id: id::RenderBundleId) -> String { - B::hub(self).render_bundles.label_for_resource(id) + pub fn render_bundle_label(&self, id: id::RenderBundleId) -> String { + A::hub(self).render_bundles.label_for_resource(id) } - pub fn render_bundle_drop(&self, render_bundle_id: id::RenderBundleId) { + pub fn render_bundle_drop(&self, render_bundle_id: id::RenderBundleId) { profiling::scope!("drop", "RenderBundle"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -4092,15 +3656,15 @@ impl Global { .push(id::Valid(render_bundle_id)); } - pub fn device_create_query_set( + pub fn device_create_query_set( &self, device_id: id::DeviceId, - desc: &wgt::QuerySetDescriptor, + desc: &resource::QuerySetDescriptor, id_in: Input, ) -> (id::QuerySetId, Option) { profiling::scope!("create_query_set", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.query_sets.prepare(id_in); @@ -4140,10 +3704,10 @@ impl Global { (id, Some(error)) } - pub fn query_set_drop(&self, query_set_id: id::QuerySetId) { + pub fn query_set_drop(&self, query_set_id: id::QuerySetId) { profiling::scope!("drop", "QuerySet"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let device_id = { @@ -4170,7 +3734,7 @@ impl Global { .push(id::Valid(query_set_id)); } - pub fn device_create_render_pipeline( + pub fn device_create_render_pipeline( &self, device_id: id::DeviceId, desc: &pipeline::RenderPipelineDescriptor, @@ -4182,7 +3746,7 @@ impl Global { ) { profiling::scope!("create_render_pipeline", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.render_pipelines.prepare(id_in); @@ -4224,7 +3788,7 @@ impl Global { /// Get an ID of one of the bind group layouts. The ID adds a refcount, /// which needs to be released by calling `bind_group_layout_drop`. - pub fn render_pipeline_get_bind_group_layout( + pub fn render_pipeline_get_bind_group_layout( &self, pipeline_id: id::RenderPipelineId, index: u32, @@ -4233,7 +3797,7 @@ impl Global { id::BindGroupLayoutId, Option, ) { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); @@ -4265,13 +3829,13 @@ impl Global { (id, Some(error)) } - pub fn render_pipeline_label(&self, id: id::RenderPipelineId) -> String { - B::hub(self).render_pipelines.label_for_resource(id) + pub fn render_pipeline_label(&self, id: id::RenderPipelineId) -> String { + A::hub(self).render_pipelines.label_for_resource(id) } - pub fn render_pipeline_drop(&self, render_pipeline_id: id::RenderPipelineId) { + pub fn render_pipeline_drop(&self, render_pipeline_id: id::RenderPipelineId) { profiling::scope!("drop", "RenderPipeline"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -4301,7 +3865,7 @@ impl Global { .push(layout_id); } - pub fn device_create_compute_pipeline( + pub fn device_create_compute_pipeline( &self, device_id: id::DeviceId, desc: &pipeline::ComputePipelineDescriptor, @@ -4313,7 +3877,7 @@ impl Global { ) { profiling::scope!("create_compute_pipeline", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.compute_pipelines.prepare(id_in); @@ -4355,7 +3919,7 @@ impl Global { /// Get an ID of one of the bind group layouts. The ID adds a refcount, /// which needs to be released by calling `bind_group_layout_drop`. - pub fn compute_pipeline_get_bind_group_layout( + pub fn compute_pipeline_get_bind_group_layout( &self, pipeline_id: id::ComputePipelineId, index: u32, @@ -4364,7 +3928,7 @@ impl Global { id::BindGroupLayoutId, Option, ) { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (pipeline_layout_guard, mut token) = hub.pipeline_layouts.read(&mut token); @@ -4396,13 +3960,13 @@ impl Global { (id, Some(error)) } - pub fn compute_pipeline_label(&self, id: id::ComputePipelineId) -> String { - B::hub(self).compute_pipelines.label_for_resource(id) + pub fn compute_pipeline_label(&self, id: id::ComputePipelineId) -> String { + A::hub(self).compute_pipelines.label_for_resource(id) } - pub fn compute_pipeline_drop(&self, compute_pipeline_id: id::ComputePipelineId) { + pub fn compute_pipeline_drop(&self, compute_pipeline_id: id::ComputePipelineId) { profiling::scope!("drop", "ComputePipeline"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); @@ -4432,17 +3996,18 @@ impl Global { .push(layout_id); } - pub fn device_create_swap_chain( + pub fn device_create_swap_chain( &self, device_id: id::DeviceId, surface_id: id::SurfaceId, desc: &wgt::SwapChainDescriptor, ) -> (id::SwapChainId, Option) { + use hal::{Adapter as _, Surface as _}; profiling::scope!("create_swap_chain", "Device"); fn validate_swap_chain_descriptor( - config: &mut hal::window::SwapchainConfig, - caps: &hal::window::SurfaceCapabilities, + config: &mut hal::SurfaceConfiguration, + caps: &hal::SurfaceCapabilities, ) -> Result<(), swap_chain::CreateSwapChainError> { let width = config.extent.width; let height = config.extent.height; @@ -4458,13 +4023,21 @@ impl Global { caps.extents ); } - if !caps.present_modes.contains(config.present_mode) { + if !caps.present_modes.contains(&config.present_mode) { log::warn!( - "Surface does not support present mode: {:?}, falling back to {:?}", + "Surface does not support present mode: {:?}, falling back to FIFO", config.present_mode, - hal::window::PresentMode::FIFO ); - config.present_mode = hal::window::PresentMode::FIFO; + config.present_mode = wgt::PresentMode::Fifo; + } + if !caps.formats.contains(&config.format) { + return Err(swap_chain::CreateSwapChainError::UnsupportedFormat { + requested: config.format, + available: caps.formats.clone(), + }); + } + if !caps.usage.contains(config.usage) { + return Err(swap_chain::CreateSwapChainError::UnsupportedUsage); } if width == 0 || height == 0 { return Err(swap_chain::CreateSwapChainError::ZeroArea); @@ -4473,8 +4046,8 @@ impl Global { } log::info!("creating swap chain {:?}", desc); - let sc_id = surface_id.to_swap_chain_id(B::VARIANT); - let hub = B::hub(self); + let sc_id = surface_id.to_swap_chain_id(A::VARIANT); + let hub = A::hub(self); let mut token = Token::root(); let (mut surface_guard, mut token) = self.surfaces.write(&mut token); @@ -4499,55 +4072,57 @@ impl Global { Err(_) => break swap_chain::CreateSwapChainError::InvalidSurface, }; - let (caps, formats) = { - let surface = B::get_surface_mut(surface); + let caps = unsafe { + let surface = A::get_surface_mut(surface); let adapter = &adapter_guard[device.adapter_id.value]; - let queue_family = &adapter.raw.queue_families[0]; - if !surface.supports_queue_family(queue_family) { - break swap_chain::CreateSwapChainError::UnsupportedQueueFamily; + match adapter.raw.adapter.surface_capabilities(surface) { + Some(caps) => caps, + None => break swap_chain::CreateSwapChainError::UnsupportedQueueFamily, } - let formats = surface.supported_formats(&adapter.raw.physical_device); - let caps = surface.capabilities(&adapter.raw.physical_device); - (caps, formats) }; let num_frames = swap_chain::DESIRED_NUM_FRAMES - .max(*caps.image_count.start()) - .min(*caps.image_count.end()); - let mut config = swap_chain::swap_chain_descriptor_to_hal( - &desc, - num_frames, - device.private_features, - ); - if let Some(formats) = formats { - if !formats.contains(&config.format) { - break swap_chain::CreateSwapChainError::UnsupportedFormat { - requested: config.format, - available: formats, - }; - } - } + .max(*caps.swap_chain_sizes.start()) + .min(*caps.swap_chain_sizes.end()); + let mut config = hal::SurfaceConfiguration { + swap_chain_size: num_frames, + present_mode: desc.present_mode, + composite_alpha_mode: hal::CompositeAlphaMode::Opaque, + format: desc.format, + extent: wgt::Extent3d { + width: desc.width, + height: desc.height, + depth_or_array_layers: 1, + }, + usage: conv::map_texture_usage(desc.usage, hal::FormatAspect::COLOR), + }; + if let Err(error) = validate_swap_chain_descriptor(&mut config, &caps) { break error; } - let framebuffer_attachment = config.framebuffer_attachment(); - match unsafe { B::get_surface_mut(surface).configure_swapchain(&device.raw, config) } { + match unsafe { A::get_surface_mut(surface).configure(&device.raw, &config) } { Ok(()) => (), - Err(hal::window::SwapchainError::OutOfMemory(_)) => { - break DeviceError::OutOfMemory.into() + Err(error) => { + break match error { + hal::SurfaceError::Outdated | hal::SurfaceError::Lost => { + swap_chain::CreateSwapChainError::InvalidSurface + } + hal::SurfaceError::Device(error) => { + swap_chain::CreateSwapChainError::Device(error.into()) + } + hal::SurfaceError::Other(message) => { + log::error!("surface configuration failed: {}", message); + swap_chain::CreateSwapChainError::InvalidSurface + } + } } - Err(hal::window::SwapchainError::DeviceLost(_)) => break DeviceError::Lost.into(), - Err(err) => panic!("failed to configure swap chain on creation: {}", err), } if let Some(sc) = swap_chain_guard.try_remove(sc_id) { - if sc.acquired_view_id.is_some() { + if sc.acquired_texture.is_some() { break swap_chain::CreateSwapChainError::SwapChainOutputExists; } - unsafe { - device.raw.destroy_semaphore(sc.semaphore); - } } let swap_chain = swap_chain::SwapChain { @@ -4558,13 +4133,9 @@ impl Global { }, desc: desc.clone(), num_frames, - semaphore: match device.raw.create_semaphore() { - Ok(sem) => sem, - Err(_) => break DeviceError::OutOfMemory.into(), - }, - acquired_view_id: None, + acquired_texture: None, active_submission_index: 0, - framebuffer_attachment, + marker: PhantomData, }; swap_chain_guard.insert(sc_id, swap_chain); @@ -4578,11 +4149,11 @@ impl Global { #[cfg(feature = "replay")] /// Only triange suspected resource IDs. This helps us to avoid ID collisions /// upon creating new resources when re-playing a trace. - pub fn device_maintain_ids( + pub fn device_maintain_ids( &self, device_id: id::DeviceId, ) -> Result<(), InvalidDevice> { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); let device = device_guard.get(device_id).map_err(|_| InvalidDevice)?; @@ -4596,12 +4167,12 @@ impl Global { Ok(()) } - pub fn device_poll( + pub fn device_poll( &self, device_id: id::DeviceId, force_wait: bool, ) -> Result<(), WaitIdleError> { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let callbacks = { let (device_guard, mut token) = hub.devices.read(&mut token); @@ -4614,17 +4185,17 @@ impl Global { Ok(()) } - fn poll_devices( + fn poll_devices( &self, force_wait: bool, callbacks: &mut Vec, ) -> Result<(), WaitIdleError> { profiling::scope!("poll_devices"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); - for (_, device) in device_guard.iter(B::VARIANT) { + for (_, device) in device_guard.iter(A::VARIANT) { let cbs = device.maintain(&hub, force_wait, &mut token)?; callbacks.extend(cbs); } @@ -4632,24 +4203,23 @@ impl Global { } pub fn poll_all_devices(&self, force_wait: bool) -> Result<(), WaitIdleError> { - use crate::backend; let mut callbacks = Vec::new(); #[cfg(vulkan)] { - self.poll_devices::(force_wait, &mut callbacks)?; + self.poll_devices::(force_wait, &mut callbacks)?; } #[cfg(metal)] { - self.poll_devices::(force_wait, &mut callbacks)?; + self.poll_devices::(force_wait, &mut callbacks)?; } #[cfg(dx12)] { - self.poll_devices::(force_wait, &mut callbacks)?; + self.poll_devices::(force_wait, &mut callbacks)?; } #[cfg(dx11)] { - self.poll_devices::(force_wait, &mut callbacks)?; + self.poll_devices::(force_wait, &mut callbacks)?; } fire_map_callbacks(callbacks); @@ -4657,32 +4227,32 @@ impl Global { Ok(()) } - pub fn device_label(&self, id: id::DeviceId) -> String { - B::hub(self).devices.label_for_resource(id) + pub fn device_label(&self, id: id::DeviceId) -> String { + A::hub(self).devices.label_for_resource(id) } - pub fn device_start_capture(&self, id: id::DeviceId) { - let hub = B::hub(self); + pub fn device_start_capture(&self, id: id::DeviceId) { + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, _) = hub.devices.read(&mut token); if let Ok(device) = device_guard.get(id) { - device.raw.start_capture(); + unsafe { device.raw.start_capture() }; } } - pub fn device_stop_capture(&self, id: id::DeviceId) { - let hub = B::hub(self); + pub fn device_stop_capture(&self, id: id::DeviceId) { + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, _) = hub.devices.read(&mut token); if let Ok(device) = device_guard.get(id) { - device.raw.stop_capture(); + unsafe { device.raw.stop_capture() }; } } - pub fn device_drop(&self, device_id: id::DeviceId) { + pub fn device_drop(&self, device_id: id::DeviceId) { profiling::scope!("drop", "Device"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device, _) = hub.devices.unregister(device_id, &mut token); if let Some(mut device) = device { @@ -4691,7 +4261,7 @@ impl Global { // Adapter is only referenced by the device and itself. // This isn't a robust way to destroy them, we should find a better one. if device.adapter_id.ref_count.load() == 1 { - let (_adapter, _) = hub + let _ = hub .adapters .unregister(device.adapter_id.value.0, &mut token); } @@ -4700,7 +4270,7 @@ impl Global { } } - pub fn buffer_map_async( + pub fn buffer_map_async( &self, buffer_id: id::BufferId, range: Range, @@ -4708,12 +4278,12 @@ impl Global { ) -> Result<(), resource::BufferAccessError> { profiling::scope!("map_async", "Buffer"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, mut token) = hub.devices.read(&mut token); let (pub_usage, internal_use) = match op.host { - HostMap::Read => (wgt::BufferUsage::MAP_READ, resource::BufferUse::MAP_READ), - HostMap::Write => (wgt::BufferUsage::MAP_WRITE, resource::BufferUse::MAP_WRITE), + HostMap::Read => (wgt::BufferUsage::MAP_READ, hal::BufferUse::MAP_READ), + HostMap::Write => (wgt::BufferUsage::MAP_WRITE, hal::BufferUse::MAP_WRITE), }; if range.start % wgt::MAP_ALIGNMENT != 0 || range.end % wgt::COPY_BUFFER_ALIGNMENT != 0 { @@ -4763,7 +4333,7 @@ impl Global { Ok(()) } - pub fn buffer_get_mapped_range( + pub fn buffer_get_mapped_range( &self, buffer_id: id::BufferId, offset: BufferAddress, @@ -4771,7 +4341,7 @@ impl Global { ) -> Result<(*mut u8, u64), resource::BufferAccessError> { profiling::scope!("get_mapped_range", "Buffer"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (buffer_guard, _) = hub.buffers.read(&mut token); let buffer = buffer_guard @@ -4804,23 +4374,17 @@ impl Global { } unsafe { Ok((ptr.as_ptr().offset(offset as isize), range_size)) } } - resource::BufferMapState::Active { - ptr, ref sub_range, .. - } => { - if offset < sub_range.offset { + resource::BufferMapState::Active { ptr, ref range, .. } => { + if offset < range.start { return Err(resource::BufferAccessError::OutOfBoundsUnderrun { index: offset, - min: sub_range.offset, + min: range.start, }); } - let range_end_offset = sub_range - .size - .map(|size| size + sub_range.offset) - .unwrap_or(buffer.size); - if offset + range_size > range_end_offset { + if offset + range_size > range.end { return Err(resource::BufferAccessError::OutOfBoundsOverrun { index: offset + range_size - 1, - max: range_end_offset, + max: range.end, }); } unsafe { Ok((ptr.as_ptr().offset(offset as isize), range_size)) } @@ -4831,13 +4395,13 @@ impl Global { } } - fn buffer_unmap_inner( + fn buffer_unmap_inner( &self, buffer_id: id::BufferId, ) -> Result, resource::BufferAccessError> { profiling::scope!("unmap", "Buffer"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut device_guard, mut token) = hub.devices.write(&mut token); @@ -4852,7 +4416,6 @@ impl Global { resource::BufferMapState::Init { ptr, stage_buffer, - stage_memory, needs_flush, } => { #[cfg(feature = "trace")] @@ -4869,48 +4432,45 @@ impl Global { }); } let _ = ptr; - if needs_flush { - stage_memory.flush_range(&device.raw, 0, None)?; + unsafe { + device + .raw + .flush_mapped_ranges(&stage_buffer, iter::once(0..buffer.size)); + } } - let &(ref buf_raw, _) = buffer + let raw_buf = buffer .raw .as_ref() .ok_or(resource::BufferAccessError::Destroyed)?; buffer.life_guard.use_at(device.active_submission_index + 1); - let region = hal::command::BufferCopy { - src: 0, - dst: 0, - size: buffer.size, - }; - let transition_src = hal::memory::Barrier::Buffer { - states: hal::buffer::Access::HOST_WRITE..hal::buffer::Access::TRANSFER_READ, - target: &stage_buffer, - range: hal::buffer::SubRange::WHOLE, - families: None, + let region = wgt::BufferSize::new(buffer.size).map(|size| hal::BufferCopy { + src_offset: 0, + dst_offset: 0, + size, + }); + let transition_src = hal::BufferBarrier { + buffer: &stage_buffer, + usage: hal::BufferUse::MAP_WRITE..hal::BufferUse::COPY_SRC, }; - let transition_dst = hal::memory::Barrier::Buffer { - states: hal::buffer::Access::empty()..hal::buffer::Access::TRANSFER_WRITE, - target: buf_raw, - range: hal::buffer::SubRange::WHOLE, - families: None, + let transition_dst = hal::BufferBarrier { + buffer: raw_buf, + usage: hal::BufferUse::empty()..hal::BufferUse::COPY_DST, }; + let encoder = device.pending_writes.activate(); unsafe { - let cmdbuf = device.borrow_pending_writes(); - cmdbuf.pipeline_barrier( - hal::pso::PipelineStage::HOST..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), + encoder.transition_buffers( iter::once(transition_src).chain(iter::once(transition_dst)), ); if buffer.size > 0 { - cmdbuf.copy_buffer(&stage_buffer, buf_raw, iter::once(region)); + encoder.copy_buffer_to_buffer(&stage_buffer, raw_buf, region.into_iter()); } } device .pending_writes - .consume_temp(queue::TempResource::Buffer(stage_buffer), stage_memory); + .consume_temp(queue::TempResource::Buffer(stage_buffer)); device.pending_writes.dst_buffers.insert(buffer_id); } resource::BufferMapState::Idle => { @@ -4919,39 +4479,40 @@ impl Global { resource::BufferMapState::Waiting(pending) => { return Ok(Some((pending.op, resource::BufferMapAsyncStatus::Aborted))); } - resource::BufferMapState::Active { - ptr, - sub_range, - host, - } => { + resource::BufferMapState::Active { ptr, range, host } => { if host == HostMap::Write { #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { let mut trace = trace.lock(); - let size = sub_range.size_to(buffer.size); + let size = range.end - range.start; let data = trace.make_binary("bin", unsafe { std::slice::from_raw_parts(ptr.as_ptr(), size as usize) }); trace.add(trace::Action::WriteBuffer { id: buffer_id, data, - range: sub_range.offset..sub_range.offset + size, + range: range.clone(), queued: false, }); } - let _ = (ptr, sub_range); + let _ = (ptr, range); } - unmap_buffer(&device.raw, buffer)?; + unsafe { + device + .raw + .unmap_buffer(buffer.raw.as_ref().unwrap()) + .map_err(DeviceError::from)? + }; } } Ok(None) } - pub fn buffer_unmap( + pub fn buffer_unmap( &self, buffer_id: id::BufferId, ) -> Result<(), resource::BufferAccessError> { - self.buffer_unmap_inner::(buffer_id) + self.buffer_unmap_inner::(buffer_id) //Note: outside inner function so no locks are held when calling the callback .map(|pending_callback| fire_map_callbacks(pending_callback.into_iter())) } diff --git a/wgpu-core/src/device/queue.rs b/wgpu-core/src/device/queue.rs index d05beaafda..1073559b7b 100644 --- a/wgpu-core/src/device/queue.rs +++ b/wgpu-core/src/device/queue.rs @@ -6,104 +6,169 @@ use crate::device::trace::Action; use crate::{ command::{ - texture_copy_view_to_hal, validate_linear_texture_data, validate_texture_copy_range, - CommandAllocator, CommandBuffer, CopySide, ImageCopyTexture, TransferError, BITS_PER_BYTE, + extract_texture_selector, validate_linear_texture_data, validate_texture_copy_range, + CommandBuffer, CopySide, ImageCopyTexture, TransferError, }, conv, - device::{alloc, DeviceError, WaitIdleError}, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Storage, Token}, + device::{DeviceError, WaitIdleError}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token}, id, memory_init_tracker::{MemoryInitKind, MemoryInitTrackerAction}, - resource::{Buffer, BufferAccessError, BufferMapState, BufferUse, TextureUse}, + resource::{Buffer, BufferAccessError, BufferMapState}, FastHashMap, FastHashSet, }; -use hal::{command::CommandBuffer as _, device::Device as _, queue::Queue as _}; +use hal::{CommandEncoder as _, Device as _, Queue as _}; +use parking_lot::Mutex; use smallvec::SmallVec; -use std::{iter, ops::Range, ptr}; +use std::{iter, mem, num::NonZeroU32, ops::Range, ptr}; use thiserror::Error; -struct StagingData { - buffer: B::Buffer, - memory: alloc::MemoryBlock, - cmdbuf: B::CommandBuffer, +/// Number of command buffers that we generate from the same pool +/// for the write_xxx commands, before the pool is recycled. +/// +/// If we don't stop at some point, the pool will grow forever, +/// without a concrete moment of when it can be cleared. +const WRITE_COMMAND_BUFFERS_PER_POOL: usize = 64; + +struct StagingData { + buffer: A::Buffer, +} + +impl StagingData { + unsafe fn write( + &self, + device: &A::Device, + offset: wgt::BufferAddress, + data: &[u8], + ) -> Result<(), hal::DeviceError> { + let mapping = device.map_buffer(&self.buffer, offset..offset + data.len() as u64)?; + ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), data.len()); + if !mapping.is_coherent { + device + .flush_mapped_ranges(&self.buffer, iter::once(offset..offset + data.len() as u64)); + } + device.unmap_buffer(&self.buffer)?; + Ok(()) + } } #[derive(Debug)] -pub enum TempResource { - Buffer(B::Buffer), - Image(B::Image), +pub enum TempResource { + Buffer(A::Buffer), + Texture(A::Texture), +} + +/// A queue execution for a particular command encoder. +pub(super) struct EncoderInFlight { + raw: A::CommandEncoder, + cmd_buffers: Vec, +} + +impl EncoderInFlight { + pub(super) unsafe fn land(mut self) -> A::CommandEncoder { + self.raw.reset_all(self.cmd_buffers.into_iter()); + self.raw + } } #[derive(Debug)] -pub(crate) struct PendingWrites { - pub command_buffer: Option, - pub temp_resources: Vec<(TempResource, alloc::MemoryBlock)>, +pub(crate) struct PendingWrites { + pub command_encoder: A::CommandEncoder, + pub is_active: bool, + pub temp_resources: Vec>, pub dst_buffers: FastHashSet, pub dst_textures: FastHashSet, + pub executing_command_buffers: Vec, } -impl PendingWrites { - pub fn new() -> Self { +impl PendingWrites { + pub fn new(command_encoder: A::CommandEncoder) -> Self { Self { - command_buffer: None, + command_encoder, + is_active: false, temp_resources: Vec::new(), dst_buffers: FastHashSet::default(), dst_textures: FastHashSet::default(), + executing_command_buffers: Vec::new(), } } - pub fn dispose( - self, - device: &B::Device, - cmd_allocator: &CommandAllocator, - mem_allocator: &mut alloc::MemoryAllocator, - ) { - if let Some(raw) = self.command_buffer { - cmd_allocator.discard_internal(raw); + pub fn dispose(mut self, device: &A::Device) { + unsafe { + if self.is_active { + self.command_encoder.discard_encoding(); + } + self.command_encoder + .reset_all(self.executing_command_buffers.into_iter()); + device.destroy_command_encoder(self.command_encoder); } - for (resource, memory) in self.temp_resources { - mem_allocator.free(device, memory); + + for resource in self.temp_resources { match resource { TempResource::Buffer(buffer) => unsafe { device.destroy_buffer(buffer); }, - TempResource::Image(image) => unsafe { - device.destroy_image(image); + TempResource::Texture(texture) => unsafe { + device.destroy_texture(texture); }, } } } - pub fn consume_temp(&mut self, resource: TempResource, memory: alloc::MemoryBlock) { - self.temp_resources.push((resource, memory)); + pub fn consume_temp(&mut self, resource: TempResource) { + self.temp_resources.push(resource); } - fn consume(&mut self, stage: StagingData) { - self.temp_resources - .push((TempResource::Buffer(stage.buffer), stage.memory)); - self.command_buffer = Some(stage.cmdbuf); + fn consume(&mut self, stage: StagingData) { + self.temp_resources.push(TempResource::Buffer(stage.buffer)); } #[must_use] - fn finish(&mut self) -> Option { + fn pre_submit(&mut self) -> Option<&A::CommandBuffer> { self.dst_buffers.clear(); self.dst_textures.clear(); - self.command_buffer.take().map(|mut cmd_buf| unsafe { - cmd_buf.finish(); - cmd_buf - }) + if self.is_active { + let cmd_buf = unsafe { self.command_encoder.end_encoding().unwrap() }; + self.is_active = false; + self.executing_command_buffers.push(cmd_buf); + self.executing_command_buffers.last() + } else { + None + } + } + + #[must_use] + fn post_submit( + &mut self, + command_allocator: &Mutex>, + device: &A::Device, + queue: &A::Queue, + ) -> Option> { + if self.executing_command_buffers.len() >= WRITE_COMMAND_BUFFERS_PER_POOL { + let new_encoder = command_allocator + .lock() + .acquire_encoder(device, queue) + .unwrap(); + Some(EncoderInFlight { + raw: mem::replace(&mut self.command_encoder, new_encoder), + cmd_buffers: mem::take(&mut self.executing_command_buffers), + }) + } else { + None + } } - fn borrow_cmd_buf(&mut self, cmd_allocator: &CommandAllocator) -> &mut B::CommandBuffer { - if self.command_buffer.is_none() { - let mut cmdbuf = cmd_allocator.allocate_internal(); + pub fn activate(&mut self) -> &mut A::CommandEncoder { + if !self.is_active { unsafe { - cmdbuf.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT); + self.command_encoder + .begin_encoding(Some("_PendingWrites")) + .unwrap(); } - self.command_buffer = Some(cmdbuf); + self.is_active = true; } - self.command_buffer.as_mut().unwrap() + &mut self.command_encoder } } @@ -113,10 +178,10 @@ struct RequiredBufferInits { } impl RequiredBufferInits { - fn add( + fn add( &mut self, buffer_memory_init_actions: &[MemoryInitTrackerAction], - buffer_guard: &mut Storage, id::BufferId>, + buffer_guard: &mut Storage, id::BufferId>, ) -> Result<(), QueueSubmitError> { for buffer_use in buffer_memory_init_actions.iter() { let buffer = buffer_guard @@ -140,65 +205,29 @@ impl RequiredBufferInits { } } -impl super::Device { - pub fn borrow_pending_writes(&mut self) -> &mut B::CommandBuffer { - self.pending_writes.borrow_cmd_buf(&self.cmd_allocator) - } - - fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result, DeviceError> { +impl super::Device { + fn prepare_stage(&mut self, size: wgt::BufferAddress) -> Result, DeviceError> { profiling::scope!("prepare_stage"); - let mut buffer = unsafe { - self.raw - .create_buffer( - size, - hal::buffer::Usage::TRANSFER_SRC, - hal::memory::SparseFlags::empty(), - ) - .map_err(|err| match err { - hal::buffer::CreationError::OutOfMemory(_) => DeviceError::OutOfMemory, - _ => panic!("failed to create staging buffer: {}", err), - })? - }; - //TODO: do we need to transition into HOST_WRITE access first? - let requirements = unsafe { - self.raw.set_buffer_name(&mut buffer, ""); - self.raw.get_buffer_requirements(&buffer) - }; - - let block = self.mem_allocator.lock().allocate( - &self.raw, - requirements, - gpu_alloc::UsageFlags::UPLOAD | gpu_alloc::UsageFlags::TRANSIENT, - )?; - block.bind_buffer(&self.raw, &mut buffer)?; - - let cmdbuf = match self.pending_writes.command_buffer.take() { - Some(cmdbuf) => cmdbuf, - None => { - let mut cmdbuf = self.cmd_allocator.allocate_internal(); - unsafe { - cmdbuf.begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT); - } - cmdbuf - } + let stage_desc = hal::BufferDescriptor { + label: Some("_Staging"), + size, + usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::COPY_SRC, + memory_flags: hal::MemoryFlag::TRANSIENT, }; - Ok(StagingData { - buffer, - memory: block, - cmdbuf, - }) + let buffer = unsafe { self.raw.create_buffer(&stage_desc)? }; + Ok(StagingData { buffer }) } fn initialize_buffer_memory( &mut self, mut required_buffer_inits: RequiredBufferInits, - buffer_guard: &mut Storage, id::BufferId>, + buffer_guard: &mut Storage, id::BufferId>, ) -> Result<(), QueueSubmitError> { self.pending_writes .dst_buffers .extend(required_buffer_inits.map.keys()); - let cmd_buf = self.pending_writes.borrow_cmd_buf(&self.cmd_allocator); + let encoder = self.pending_writes.activate(); let mut trackers = self.trackers.lock(); for (buffer_id, mut ranges) in required_buffer_inits.map.drain() { @@ -217,35 +246,23 @@ impl super::Device { let transition = trackers.buffers.change_replace_tracked( id::Valid(buffer_id), (), - BufferUse::COPY_DST, + hal::BufferUse::COPY_DST, ); let buffer = buffer_guard.get(buffer_id).unwrap(); - let &(ref buffer_raw, _) = buffer + let raw_buf = buffer .raw .as_ref() .ok_or(QueueSubmitError::DestroyedBuffer(buffer_id))?; unsafe { - cmd_buf.pipeline_barrier( - super::all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - transition.map(|pending| pending.into_hal(buffer)), - ); + encoder.transition_buffers(transition.map(|pending| pending.into_hal(buffer))); } - for range in ranges { - let size = range.end - range.start; - assert!(range.start % 4 == 0, "Buffer {:?} has an uninitialized range with a start not aligned to 4 (start was {})", buffer, range.start); - assert!(size % 4 == 0, "Buffer {:?} has an uninitialized range with a size not aligned to 4 (size was {})", buffer, size); + for range in ranges { + assert!(range.start % 4 == 0, "Buffer {:?} has an uninitialized range with a start not aligned to 4 (start was {})", raw_buf, range.start); + assert!(range.end % 4 == 0, "Buffer {:?} has an uninitialized range with an end not aligned to 4 (end was {})", raw_buf, range.end); unsafe { - cmd_buf.fill_buffer( - buffer_raw, - hal::buffer::SubRange { - offset: range.start, - size: Some(size), - }, - 0, - ); + encoder.fill_buffer(raw_buf, range, 0); } } } @@ -285,7 +302,7 @@ pub enum QueueSubmitError { //TODO: move out common parts of write_xxx. impl Global { - pub fn queue_write_buffer( + pub fn queue_write_buffer( &self, queue_id: id::QueueId, buffer_id: id::BufferId, @@ -294,7 +311,7 @@ impl Global { ) -> Result<(), QueueWriteError> { profiling::scope!("write_buffer", "Queue"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut device_guard, mut token) = hub.devices.write(&mut token); let device = device_guard @@ -320,15 +337,15 @@ impl Global { return Ok(()); } - let mut stage = device.prepare_stage(data_size)?; - stage.memory.write_bytes(&device.raw, 0, data)?; + let stage = device.prepare_stage(data_size)?; + unsafe { stage.write(&device.raw, 0, data) }.map_err(DeviceError::from)?; let mut trackers = device.trackers.lock(); let (dst, transition) = trackers .buffers - .use_replace(&*buffer_guard, buffer_id, (), BufferUse::COPY_DST) + .use_replace(&*buffer_guard, buffer_id, (), hal::BufferUse::COPY_DST) .map_err(TransferError::InvalidBuffer)?; - let &(ref dst_raw, _) = dst + let dst_raw = dst .raw .as_ref() .ok_or(TransferError::InvalidBuffer(buffer_id))?; @@ -353,26 +370,20 @@ impl Global { .into()); } - let region = hal::command::BufferCopy { - src: 0, - dst: buffer_offset, - size: data.len() as _, - }; + let region = wgt::BufferSize::new(data.len() as u64).map(|size| hal::BufferCopy { + src_offset: 0, + dst_offset: buffer_offset, + size, + }); + let barriers = iter::once(hal::BufferBarrier { + buffer: &stage.buffer, + usage: hal::BufferUse::MAP_WRITE..hal::BufferUse::COPY_SRC, + }) + .chain(transition.map(|pending| pending.into_hal(dst))); + let encoder = device.pending_writes.activate(); unsafe { - stage.cmdbuf.pipeline_barrier( - super::all_buffer_stages()..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - iter::once(hal::memory::Barrier::Buffer { - states: hal::buffer::Access::HOST_WRITE..hal::buffer::Access::TRANSFER_READ, - target: &stage.buffer, - range: hal::buffer::SubRange::WHOLE, - families: None, - }) - .chain(transition.map(|pending| pending.into_hal(dst))), - ); - stage - .cmdbuf - .copy_buffer(&stage.buffer, dst_raw, iter::once(region)); + encoder.transition_buffers(barriers); + encoder.copy_buffer_to_buffer(&stage.buffer, dst_raw, region.into_iter()); } device.pending_writes.consume(stage); @@ -391,7 +402,7 @@ impl Global { Ok(()) } - pub fn queue_write_texture( + pub fn queue_write_texture( &self, queue_id: id::QueueId, destination: &ImageCopyTexture, @@ -401,15 +412,12 @@ impl Global { ) -> Result<(), QueueWriteError> { profiling::scope!("write_texture", "Queue"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut device_guard, mut token) = hub.devices.write(&mut token); let device = device_guard .get_mut(queue_id) .map_err(|_| DeviceError::Invalid)?; - let (texture_guard, _) = hub.textures.read(&mut token); - let (image_layers, image_range, image_offset) = - texture_copy_view_to_hal(destination, size, &*texture_guard)?; #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { @@ -428,49 +436,48 @@ impl Global { return Ok(()); } - let texture_format = texture_guard.get(destination.texture).unwrap().format; - let bytes_per_block = conv::map_texture_format(texture_format, device.private_features) - .surface_desc() - .bits as u32 - / BITS_PER_BYTE; + let (texture_guard, _) = hub.textures.read(&mut token); + let (selector, texture_base, texture_format) = + extract_texture_selector(destination, size, &*texture_guard)?; + let format_desc = texture_format.describe(); validate_linear_texture_data( data_layout, texture_format, data.len() as wgt::BufferAddress, CopySide::Source, - bytes_per_block as wgt::BufferAddress, + format_desc.block_size as wgt::BufferAddress, size, false, )?; - let (block_width, block_height) = texture_format.describe().block_dimensions; - let block_width = block_width as u32; - let block_height = block_height as u32; - if !conv::is_valid_copy_dst_texture_format(texture_format) { return Err(TransferError::CopyToForbiddenTextureFormat(texture_format).into()); } - let width_blocks = size.width / block_width; - let height_blocks = size.height / block_width; + let (block_width, block_height) = format_desc.block_dimensions; + let width_blocks = size.width / block_width as u32; + let height_blocks = size.height / block_height as u32; - let texel_rows_per_image = if let Some(rows_per_image) = data_layout.rows_per_image { - rows_per_image.get() - } else { - // doesn't really matter because we need this only if we copy more than one layer, and then we validate for this being not None - size.height + let block_rows_per_image = match data_layout.rows_per_image { + Some(rows_per_image) => rows_per_image.get(), + None => { + // doesn't really matter because we need this only if we copy more than one layer, and then we validate for this being not None + size.height + } }; - let block_rows_per_image = texel_rows_per_image / block_height; let bytes_per_row_alignment = get_lowest_common_denom( - device.hal_limits.optimal_buffer_copy_pitch_alignment as u32, - bytes_per_block, + device.alignments.buffer_copy_pitch.get() as u32, + format_desc.block_size as u32, + ); + let stage_bytes_per_row = align_to( + format_desc.block_size as u32 * width_blocks, + bytes_per_row_alignment, ); - let stage_bytes_per_row = align_to(bytes_per_block * width_blocks, bytes_per_row_alignment); let block_rows_in_copy = (size.depth_or_array_layers - 1) * block_rows_per_image + height_blocks; let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64; - let mut stage = device.prepare_stage(stage_size)?; + let stage = device.prepare_stage(stage_size)?; let mut trackers = device.trackers.lock(); let (dst, transition) = trackers @@ -478,42 +485,37 @@ impl Global { .use_replace( &*texture_guard, destination.texture, - image_range, - TextureUse::COPY_DST, + selector, + hal::TextureUse::COPY_DST, ) .unwrap(); - let &(ref dst_raw, _) = dst + let dst_raw = dst .raw .as_ref() .ok_or(TransferError::InvalidTexture(destination.texture))?; - if !dst.usage.contains(wgt::TextureUsage::COPY_DST) { + if !dst.desc.usage.contains(wgt::TextureUsage::COPY_DST) { return Err( TransferError::MissingCopyDstUsageFlag(None, Some(destination.texture)).into(), ); } - validate_texture_copy_range( - destination, - dst.format, - dst.kind, - CopySide::Destination, - size, - )?; + let max_image_extent = + validate_texture_copy_range(destination, &dst.desc, CopySide::Destination, size)?; dst.life_guard.use_at(device.active_submission_index + 1); let bytes_per_row = if let Some(bytes_per_row) = data_layout.bytes_per_row { bytes_per_row.get() } else { - width_blocks * bytes_per_block + width_blocks * format_desc.block_size as u32 }; - let ptr = stage.memory.map(&device.raw, 0, stage_size)?; + let mapping = unsafe { device.raw.map_buffer(&stage.buffer, 0..stage_size) } + .map_err(DeviceError::from)?; unsafe { profiling::scope!("copy"); - //TODO: https://github.com/zakarumych/gpu-alloc/issues/13 if stage_bytes_per_row == bytes_per_row { // Fast path if the data isalready being aligned optimally. - ptr::copy_nonoverlapping(data.as_ptr(), ptr.as_ptr(), stage_size as usize); + ptr::copy_nonoverlapping(data.as_ptr(), mapping.ptr.as_ptr(), stage_size as usize); } else { // Copy row by row into the optimal alignment. let copy_bytes_per_row = stage_bytes_per_row.min(bytes_per_row) as usize; @@ -523,7 +525,7 @@ impl Global { ptr::copy_nonoverlapping( data.as_ptr() .offset((rows_offset + row) as isize * bytes_per_row as isize), - ptr.as_ptr().offset( + mapping.ptr.as_ptr().offset( (rows_offset + row) as isize * stage_bytes_per_row as isize, ), copy_bytes_per_row, @@ -532,49 +534,45 @@ impl Global { } } } - stage.memory.unmap(&device.raw); - if !stage.memory.is_coherent() { - stage.memory.flush_range(&device.raw, 0, None)?; + unsafe { + if !mapping.is_coherent { + device + .raw + .flush_mapped_ranges(&stage.buffer, iter::once(0..stage_size)); + } + device + .raw + .unmap_buffer(&stage.buffer) + .map_err(DeviceError::from)?; } // WebGPU uses the physical size of the texture for copies whereas vulkan uses // the virtual size. We have passed validation, so it's safe to use the // image extent data directly. We want the provided copy size to be no larger than // the virtual size. - let max_image_extent = dst.kind.level_extent(destination.mip_level as _); - let image_extent = wgt::Extent3d { - width: size.width.min(max_image_extent.width), - height: size.height.min(max_image_extent.height), - depth_or_array_layers: size.depth_or_array_layers, + let region = hal::BufferTextureCopy { + buffer_layout: wgt::ImageDataLayout { + offset: 0, + bytes_per_row: NonZeroU32::new(stage_bytes_per_row), + rows_per_image: NonZeroU32::new(block_rows_per_image), + }, + texture_base, + size: wgt::Extent3d { + width: size.width.min(max_image_extent.width), + height: size.height.min(max_image_extent.height), + depth_or_array_layers: size.depth_or_array_layers, + }, }; - let region = hal::command::BufferImageCopy { - buffer_offset: 0, - buffer_width: (stage_bytes_per_row / bytes_per_block) * block_width, - buffer_height: texel_rows_per_image, - image_layers, - image_offset, - image_extent: conv::map_extent(&image_extent, dst.dimension), + let barrier = hal::BufferBarrier { + buffer: &stage.buffer, + usage: hal::BufferUse::MAP_WRITE..hal::BufferUse::COPY_SRC, }; + let encoder = device.pending_writes.activate(); unsafe { - stage.cmdbuf.pipeline_barrier( - super::all_image_stages() | hal::pso::PipelineStage::HOST - ..hal::pso::PipelineStage::TRANSFER, - hal::memory::Dependencies::empty(), - iter::once(hal::memory::Barrier::Buffer { - states: hal::buffer::Access::HOST_WRITE..hal::buffer::Access::TRANSFER_READ, - target: &stage.buffer, - range: hal::buffer::SubRange::WHOLE, - families: None, - }) - .chain(transition.map(|pending| pending.into_hal(dst))), - ); - stage.cmdbuf.copy_buffer_to_image( - &stage.buffer, - dst_raw, - hal::image::Layout::TransferDstOptimal, - iter::once(region), - ); + encoder.transition_buffers(iter::once(barrier)); + encoder.transition_textures(transition.map(|pending| pending.into_hal(dst))); + encoder.copy_buffer_to_texture(&stage.buffer, dst_raw, iter::once(region)); } device.pending_writes.consume(stage); @@ -586,14 +584,14 @@ impl Global { Ok(()) } - pub fn queue_submit( + pub fn queue_submit( &self, queue_id: id::QueueId, command_buffer_ids: &[id::CommandBufferId], ) -> Result<(), QueueSubmitError> { profiling::scope!("submit", "Queue"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let callbacks = { @@ -601,12 +599,12 @@ impl Global { let device = device_guard .get_mut(queue_id) .map_err(|_| DeviceError::Invalid)?; - let pending_write_command_buffer = device.pending_writes.finish(); device.temp_suspected.clear(); device.active_submission_index += 1; let submit_index = device.active_submission_index; + let mut active_executions = Vec::new(); - let fence = { + { let mut signal_swapchain_semaphores = SmallVec::<[_; 1]>::new(); let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); let (mut command_buffer_guard, mut token) = hub.command_buffers.write(&mut token); @@ -634,9 +632,12 @@ impl Global { // finish all the command buffers first for &cmb_id in command_buffer_ids { - let cmdbuf = match command_buffer_guard.get_mut(cmb_id) { - Ok(cmdbuf) => cmdbuf, - Err(_) => continue, + let mut cmdbuf = match hub + .command_buffers + .unregister_locked(cmb_id, &mut *command_buffer_guard) + { + Some(cmdbuf) => cmdbuf, + None => continue, }; #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { @@ -646,6 +647,7 @@ impl Global { )); } if !cmdbuf.is_finished() { + device.destroy_command_buffer(cmdbuf); continue; } @@ -656,7 +658,7 @@ impl Global { for sc_id in cmdbuf.used_swap_chains.drain(..) { let sc = &mut swap_chain_guard[sc_id.value]; - if sc.acquired_view_id.is_none() { + if sc.acquired_texture.is_none() { return Err(QueueSubmitError::SwapChainOutputDropped); } if sc.active_submission_index != submit_index { @@ -670,13 +672,17 @@ impl Global { // update submission IDs for id in cmdbuf.trackers.buffers.used() { let buffer = &mut buffer_guard[id]; - if buffer.raw.is_none() { - return Err(QueueSubmitError::DestroyedBuffer(id.0)); - } + let raw_buf = match buffer.raw { + Some(ref raw) => raw, + None => { + return Err(QueueSubmitError::DestroyedBuffer(id.0)); + } + }; if !buffer.life_guard.use_at(submit_index) { if let BufferMapState::Active { .. } = buffer.map_state { log::warn!("Dropped buffer has a pending mapping."); - super::unmap_buffer(&device.raw, buffer)?; + unsafe { device.raw.unmap_buffer(raw_buf) } + .map_err(DeviceError::from)?; } device.temp_suspected.buffers.push(id); } else { @@ -726,28 +732,31 @@ impl Global { } } + let mut baked = cmdbuf.into_baked(); + // execute resource transitions - let mut transit = device.cmd_allocator.extend(cmdbuf); unsafe { - // the last buffer was open, closing now - cmdbuf.raw.last_mut().unwrap().finish(); - transit - .begin_primary(hal::command::CommandBufferFlags::ONE_TIME_SUBMIT); - } + baked + .encoder + .begin_encoding(Some("_Transit")) + .map_err(DeviceError::from)? + }; log::trace!("Stitching command buffer {:?} before submission", cmb_id); - trackers.merge_extend_stateless(&cmdbuf.trackers); + trackers.merge_extend_stateless(&baked.trackers); CommandBuffer::insert_barriers( - &mut transit, + &mut baked.encoder, &mut *trackers, - &cmdbuf.trackers.buffers, - &cmdbuf.trackers.textures, + &baked.trackers.buffers, + &baked.trackers.textures, &*buffer_guard, &*texture_guard, ); - unsafe { - transit.finish(); - } - cmdbuf.raw.insert(0, transit); + let transit = unsafe { baked.encoder.end_encoding().unwrap() }; + baked.list.insert(0, transit); + active_executions.push(EncoderInFlight { + raw: baked.encoder, + cmd_buffers: baked.list, + }); } log::trace!("Device after submission {}: {:#?}", submit_index, trackers); @@ -758,41 +767,26 @@ impl Global { } } - // now prepare the GPU submission - let mut fence = device - .raw - .create_fence(false) - .or(Err(DeviceError::OutOfMemory))?; - let signal_semaphores = signal_swapchain_semaphores + let super::Device { + ref mut pending_writes, + ref mut queue, + ref mut fence, + .. + } = *device; + let refs = pending_writes + .pre_submit() .into_iter() - .map(|sc_id| &swap_chain_guard[sc_id].semaphore); - //Note: we could technically avoid the heap Vec here - let mut command_buffers = Vec::new(); - command_buffers.extend(pending_write_command_buffer.as_ref()); - for &cmd_buf_id in command_buffer_ids.iter() { - match command_buffer_guard.get(cmd_buf_id) { - Ok(cmd_buf) if cmd_buf.is_finished() => { - command_buffers.extend(cmd_buf.raw.iter()); - } - _ => {} - } - } - + .chain( + active_executions + .iter() + .flat_map(|pool_execution| pool_execution.cmd_buffers.iter()), + ) + .collect::>(); unsafe { - device.queue_group.queues[0].submit( - command_buffers.into_iter(), - iter::empty(), - signal_semaphores, - Some(&mut fence), - ); + queue + .submit(&refs, Some((fence, submit_index))) + .map_err(DeviceError::from)?; } - fence - }; - - if let Some(comb_raw) = pending_write_command_buffer { - device - .cmd_allocator - .after_submit_internal(comb_raw, submit_index); } let callbacks = match device.maintain(&hub, false, &mut token) { @@ -802,22 +796,20 @@ impl Global { }; profiling::scope!("cleanup"); + if let Some(pending_execution) = device.pending_writes.post_submit( + &device.command_allocator, + &device.raw, + &device.queue, + ) { + active_executions.push(pending_execution); + } super::Device::lock_life_internal(&device.life_tracker, &mut token).track_submission( submit_index, - fence, &device.temp_suspected, device.pending_writes.temp_resources.drain(..), + active_executions, ); - // finally, return the command buffers to the allocator - for &cmb_id in command_buffer_ids { - if let (Some(cmd_buf), _) = hub.command_buffers.unregister(cmb_id, &mut token) { - device - .cmd_allocator - .after_submit(cmd_buf, &device.raw, submit_index); - } - } - callbacks }; @@ -828,15 +820,15 @@ impl Global { Ok(()) } - pub fn queue_get_timestamp_period( + pub fn queue_get_timestamp_period( &self, queue_id: id::QueueId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (device_guard, _) = hub.devices.read(&mut token); match device_guard.get(queue_id) { - Ok(device) => Ok(device.queue_group.queues[0].timestamp_period()), + Ok(_device) => Ok(1.0), //TODO? Err(_) => Err(InvalidQueue), } } diff --git a/wgpu-core/src/device/trace.rs b/wgpu-core/src/device/trace.rs index 340dc8d4aa..ce4571db88 100644 --- a/wgpu-core/src/device/trace.rs +++ b/wgpu-core/src/device/trace.rs @@ -22,7 +22,7 @@ pub(crate) fn new_render_bundle_encoder_descriptor<'a>( label, color_formats: Cow::Borrowed(&context.attachments.colors), depth_stencil_format: context.attachments.depth_stencil, - sample_count: context.sample_count as u32, + sample_count: context.sample_count, } } @@ -98,7 +98,7 @@ pub enum Action<'a> { DestroyRenderBundle(id::RenderBundleId), CreateQuerySet { id: id::QuerySetId, - desc: wgt::QuerySetDescriptor, + desc: crate::resource::QuerySetDescriptor<'a>, }, DestroyQuerySet(id::QuerySetId), WriteBuffer { diff --git a/wgpu-core/src/hub.rs b/wgpu-core/src/hub.rs index 3d92bfeb63..17dd39e746 100644 --- a/wgpu-core/src/hub.rs +++ b/wgpu-core/src/hub.rs @@ -3,18 +3,13 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - backend, binding_model::{BindGroup, BindGroupLayout, PipelineLayout}, command::{CommandBuffer, RenderBundle}, device::Device, - id::{ - AdapterId, BindGroupId, BindGroupLayoutId, BufferId, CommandBufferId, ComputePipelineId, - DeviceId, PipelineLayoutId, RenderBundleId, RenderPipelineId, SamplerId, ShaderModuleId, - SurfaceId, SwapChainId, TextureId, TextureViewId, TypedId, Valid, - }, + id, instance::{Adapter, Instance, Surface}, pipeline::{ComputePipeline, RenderPipeline, ShaderModule}, - resource::{Buffer, Sampler, Texture, TextureView}, + resource::{Buffer, QuerySet, Sampler, Texture, TextureView}, swap_chain::SwapChain, Epoch, Index, }; @@ -22,8 +17,6 @@ use crate::{ use parking_lot::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; use wgt::Backend; -use crate::id::QuerySetId; -use crate::resource::QuerySet; #[cfg(debug_assertions)] use std::cell::Cell; use std::{fmt::Debug, marker::PhantomData, ops}; @@ -52,7 +45,7 @@ impl IdentityManager { } } - pub fn alloc(&mut self, backend: Backend) -> I { + pub fn alloc(&mut self, backend: Backend) -> I { match self.free.pop() { Some(index) => I::zip(index, self.epochs[index as usize], backend), None => { @@ -64,7 +57,7 @@ impl IdentityManager { } } - pub fn free(&mut self, id: I) { + pub fn free(&mut self, id: I) { let (index, epoch, _backend) = id.unzip(); // avoid doing this check in release if cfg!(debug_assertions) { @@ -88,26 +81,26 @@ enum Element { pub(crate) struct InvalidId; #[derive(Debug)] -pub struct Storage { +pub struct Storage { map: Vec>, kind: &'static str, _phantom: PhantomData, } -impl ops::Index> for Storage { +impl ops::Index> for Storage { type Output = T; - fn index(&self, id: Valid) -> &T { + fn index(&self, id: id::Valid) -> &T { self.get(id.0).unwrap() } } -impl ops::IndexMut> for Storage { - fn index_mut(&mut self, id: Valid) -> &mut T { +impl ops::IndexMut> for Storage { + fn index_mut(&mut self, id: id::Valid) -> &mut T { self.get_mut(id.0).unwrap() } } -impl Storage { +impl Storage { pub(crate) fn contains(&self, id: I) -> bool { let (index, epoch, _) = id.unzip(); match self.map[index as usize] { @@ -226,7 +219,7 @@ impl Storage { } /// Type system for enforcing the lock order on shared HUB structures. -/// If type A implements `Access`, that means we are allowed to proceed +/// If type A implements `Access`, that means we are allowed to proceed /// with locking resource `B` after we lock `A`. /// /// The implenentations basically describe the edges in a directed graph @@ -234,66 +227,66 @@ impl Storage { /// multiple concurrent paths on this graph (from multiple threads) without /// deadlocks, i.e. there is always a path whose next resource is not locked /// by some other path, at any time. -pub trait Access {} +pub trait Access {} pub enum Root {} //TODO: establish an order instead of declaring all the pairs. impl Access for Root {} impl Access for Root {} impl Access for Instance {} -impl Access> for Root {} -impl Access> for Surface {} -impl Access> for Root {} -impl Access> for Surface {} -impl Access> for Adapter {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for RenderBundle {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for PipelineLayout {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for BindGroupLayout {} -impl Access> for PipelineLayout {} -impl Access> for CommandBuffer {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for SwapChain {} -impl Access for Device {} -impl Access for CommandBuffer {} -impl Access> for Device {} -impl Access> for BindGroup {} -impl Access> for Device {} -impl Access> for BindGroup {} -impl Access> for ComputePipeline {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for CommandBuffer {} -impl Access> for RenderPipeline {} -impl Access> for ComputePipeline {} -impl Access> for Device {} -impl Access> for BindGroupLayout {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for BindGroupLayout {} -impl Access> for BindGroup {} -impl Access> for CommandBuffer {} -impl Access> for ComputePipeline {} -impl Access> for RenderPipeline {} -impl Access> for QuerySet {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for Buffer {} -impl Access> for Root {} -impl Access> for SwapChain {} -impl Access> for Device {} -impl Access> for Texture {} -impl Access> for Root {} -impl Access> for Device {} -impl Access> for TextureView {} +impl Access> for Root {} +impl Access> for Surface {} +impl Access> for Root {} +impl Access> for Surface {} +impl Access> for Adapter {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for RenderBundle {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for PipelineLayout {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for BindGroupLayout {} +impl Access> for PipelineLayout {} +impl Access> for CommandBuffer {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for SwapChain {} //TODO: remove this (only used in `submit()`) +impl Access for Device {} +impl Access for CommandBuffer {} +impl Access> for Device {} +impl Access> for BindGroup {} +impl Access> for Device {} +impl Access> for BindGroup {} +impl Access> for ComputePipeline {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for CommandBuffer {} +impl Access> for RenderPipeline {} +impl Access> for ComputePipeline {} +impl Access> for Device {} +impl Access> for BindGroupLayout {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for BindGroupLayout {} +impl Access> for BindGroup {} +impl Access> for CommandBuffer {} +impl Access> for ComputePipeline {} +impl Access> for RenderPipeline {} +impl Access> for QuerySet {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for Buffer {} +impl Access> for Root {} +impl Access> for SwapChain {} //TODO: remove this (only used in `get_next_texture()`) +impl Access> for Device {} +impl Access> for Texture {} +impl Access> for Root {} +impl Access> for Device {} +impl Access> for TextureView {} #[cfg(debug_assertions)] thread_local! { @@ -348,7 +341,7 @@ pub trait IdentityHandler: Debug { fn free(&self, id: I); } -impl IdentityHandler for Mutex { +impl IdentityHandler for Mutex { type Input = PhantomData; fn process(&self, _id: Self::Input, backend: Backend) -> I { self.lock().alloc(backend) @@ -366,7 +359,7 @@ pub trait IdentityHandlerFactory { #[derive(Debug)] pub struct IdentityManagerFactory; -impl IdentityHandlerFactory for IdentityManagerFactory { +impl IdentityHandlerFactory for IdentityManagerFactory { type Filter = Mutex; fn spawn(&self, min_index: Index) -> Self::Filter { Mutex::new(IdentityManager::from_index(min_index)) @@ -374,23 +367,23 @@ impl IdentityHandlerFactory for IdentityManagerFactory { } pub trait GlobalIdentityHandlerFactory: - IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory - + IdentityHandlerFactory + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory + + IdentityHandlerFactory { } @@ -410,13 +403,13 @@ pub trait Resource { } #[derive(Debug)] -pub struct Registry> { +pub struct Registry> { identity: F::Filter, data: RwLock>, backend: Backend, } -impl> Registry { +impl> Registry { fn new(backend: Backend, factory: &F) -> Self { Self { identity: factory.spawn(0), @@ -443,12 +436,12 @@ impl> Registry { } #[must_use] -pub(crate) struct FutureId<'a, I: TypedId, T> { +pub(crate) struct FutureId<'a, I: id::TypedId, T> { id: I, data: &'a RwLock>, } -impl FutureId<'_, I, T> { +impl FutureId<'_, I, T> { #[cfg(feature = "trace")] pub fn id(&self) -> I { self.id @@ -458,9 +451,9 @@ impl FutureId<'_, I, T> { self.id } - pub fn assign<'a, A: Access>(self, value: T, _: &'a mut Token) -> Valid { + pub fn assign<'a, A: Access>(self, value: T, _: &'a mut Token) -> id::Valid { self.data.write().insert(self.id, value); - Valid(self.id) + id::Valid(self.id) } pub fn assign_error<'a, A: Access>(self, label: &str, _: &'a mut Token) -> I { @@ -469,7 +462,7 @@ impl FutureId<'_, I, T> { } } -impl> Registry { +impl> Registry { pub(crate) fn prepare( &self, id_in: >::Input, @@ -536,56 +529,55 @@ impl> Registry { - pub adapters: Registry, AdapterId, F>, - pub devices: Registry, DeviceId, F>, - pub swap_chains: Registry, SwapChainId, F>, - pub pipeline_layouts: Registry, PipelineLayoutId, F>, - pub shader_modules: Registry, ShaderModuleId, F>, - pub bind_group_layouts: Registry, BindGroupLayoutId, F>, - pub bind_groups: Registry, BindGroupId, F>, - pub command_buffers: Registry, CommandBufferId, F>, - pub render_bundles: Registry, - pub render_pipelines: Registry, RenderPipelineId, F>, - pub compute_pipelines: Registry, ComputePipelineId, F>, - pub query_sets: Registry, QuerySetId, F>, - pub buffers: Registry, BufferId, F>, - pub textures: Registry, TextureId, F>, - pub texture_views: Registry, TextureViewId, F>, - pub samplers: Registry, SamplerId, F>, -} - -impl Hub { +pub struct Hub { + pub adapters: Registry, id::AdapterId, F>, + pub devices: Registry, id::DeviceId, F>, + pub swap_chains: Registry, id::SwapChainId, F>, + pub pipeline_layouts: Registry, id::PipelineLayoutId, F>, + pub shader_modules: Registry, id::ShaderModuleId, F>, + pub bind_group_layouts: Registry, id::BindGroupLayoutId, F>, + pub bind_groups: Registry, id::BindGroupId, F>, + pub command_buffers: Registry, id::CommandBufferId, F>, + pub render_bundles: Registry, + pub render_pipelines: Registry, id::RenderPipelineId, F>, + pub compute_pipelines: Registry, id::ComputePipelineId, F>, + pub query_sets: Registry, id::QuerySetId, F>, + pub buffers: Registry, id::BufferId, F>, + pub textures: Registry, id::TextureId, F>, + pub texture_views: Registry, id::TextureViewId, F>, + pub samplers: Registry, id::SamplerId, F>, +} + +impl Hub { fn new(factory: &F) -> Self { Self { - adapters: Registry::new(B::VARIANT, factory), - devices: Registry::new(B::VARIANT, factory), - swap_chains: Registry::new(B::VARIANT, factory), - pipeline_layouts: Registry::new(B::VARIANT, factory), - shader_modules: Registry::new(B::VARIANT, factory), - bind_group_layouts: Registry::new(B::VARIANT, factory), - bind_groups: Registry::new(B::VARIANT, factory), - command_buffers: Registry::new(B::VARIANT, factory), - render_bundles: Registry::new(B::VARIANT, factory), - render_pipelines: Registry::new(B::VARIANT, factory), - compute_pipelines: Registry::new(B::VARIANT, factory), - query_sets: Registry::new(B::VARIANT, factory), - buffers: Registry::new(B::VARIANT, factory), - textures: Registry::new(B::VARIANT, factory), - texture_views: Registry::new(B::VARIANT, factory), - samplers: Registry::new(B::VARIANT, factory), - } - } -} - -impl Hub { + adapters: Registry::new(A::VARIANT, factory), + devices: Registry::new(A::VARIANT, factory), + swap_chains: Registry::new(A::VARIANT, factory), + pipeline_layouts: Registry::new(A::VARIANT, factory), + shader_modules: Registry::new(A::VARIANT, factory), + bind_group_layouts: Registry::new(A::VARIANT, factory), + bind_groups: Registry::new(A::VARIANT, factory), + command_buffers: Registry::new(A::VARIANT, factory), + render_bundles: Registry::new(A::VARIANT, factory), + render_pipelines: Registry::new(A::VARIANT, factory), + compute_pipelines: Registry::new(A::VARIANT, factory), + query_sets: Registry::new(A::VARIANT, factory), + buffers: Registry::new(A::VARIANT, factory), + textures: Registry::new(A::VARIANT, factory), + texture_views: Registry::new(A::VARIANT, factory), + samplers: Registry::new(A::VARIANT, factory), + } + } +} + +impl Hub { //TODO: instead of having a hacky `with_adapters` parameter, // we should have `clear_device(device_id)` that specifically destroys // everything related to a logical device. - fn clear(&self, surface_guard: &mut Storage, with_adapters: bool) { - use crate::resource::TextureViewInner; - use hal::{device::Device as _, window::PresentationSurface as _}; + fn clear(&self, surface_guard: &mut Storage, with_adapters: bool) { + use crate::resource::TextureViewSource; + use hal::{Device as _, Surface as _}; let mut devices = self.devices.data.write(); for element in devices.map.iter_mut() { @@ -607,14 +599,14 @@ impl Hub { let textures = self.textures.data.read(); for element in self.texture_views.data.write().map.drain(..) { if let Element::Occupied(texture_view, _) = element { - match texture_view.inner { - TextureViewInner::Native { raw, source_id } => { + match texture_view.source { + TextureViewSource::Native(source_id) => { let device = &devices[textures[source_id.value].device_id.value]; unsafe { - device.raw.destroy_image_view(raw); + device.raw.destroy_texture_view(texture_view.raw); } } - TextureViewInner::SwapChain { .. } => {} //TODO + TextureViewSource::SwapChain(_) => {} //TODO } } } @@ -634,15 +626,15 @@ impl Hub { for element in self.command_buffers.data.write().map.drain(..) { if let Element::Occupied(command_buffer, _) = element { let device = &devices[command_buffer.device_id.value]; - device - .cmd_allocator - .after_submit(command_buffer, &device.raw, 0); + device.destroy_command_buffer(command_buffer); } } for element in self.bind_groups.data.write().map.drain(..) { if let Element::Occupied(bind_group, _) = element { let device = &devices[bind_group.device_id.value]; - device.destroy_bind_group(bind_group); + unsafe { + device.raw.destroy_bind_group(bind_group.raw); + } } } @@ -658,7 +650,7 @@ impl Hub { if let Element::Occupied(bgl, _) = element { let device = &devices[bgl.device_id.value]; unsafe { - device.raw.destroy_descriptor_set_layout(bgl.raw); + device.raw.destroy_bind_group_layout(bgl.raw); } } } @@ -682,7 +674,7 @@ impl Hub { if let Element::Occupied(pipeline, _) = element { let device = &devices[pipeline.device_id.value]; unsafe { - device.raw.destroy_graphics_pipeline(pipeline.raw); + device.raw.destroy_render_pipeline(pipeline.raw); } } } @@ -690,16 +682,13 @@ impl Hub { for (index, element) in self.swap_chains.data.write().map.drain(..).enumerate() { if let Element::Occupied(swap_chain, epoch) = element { let device = &devices[swap_chain.device_id.value]; - unsafe { - device.raw.destroy_semaphore(swap_chain.semaphore); - } - let suf_id = TypedId::zip(index as Index, epoch, B::VARIANT); + let suf_id = id::TypedId::zip(index as Index, epoch, A::VARIANT); //TODO: hold the surface alive by the swapchain if surface_guard.contains(suf_id) { let surface = surface_guard.get_mut(suf_id).unwrap(); - let suf = B::get_surface_mut(surface); + let suf = A::get_surface_mut(surface); unsafe { - suf.unconfigure_swapchain(&device.raw); + suf.unconfigure(&device.raw); } } } @@ -709,7 +698,7 @@ impl Hub { if let Element::Occupied(query_set, _) = element { let device = &devices[query_set.device_id.value]; unsafe { - device.raw.destroy_query_pool(query_set.raw); + device.raw.destroy_query_set(query_set.raw); } } } @@ -719,24 +708,25 @@ impl Hub { device.dispose(); } } + if with_adapters { + drop(devices); self.adapters.data.write().map.clear(); } } } -#[derive(Debug)] pub struct Hubs { #[cfg(vulkan)] - vulkan: Hub, + vulkan: Hub, #[cfg(metal)] - metal: Hub, + metal: Hub, #[cfg(dx12)] - dx12: Hub, + dx12: Hub, #[cfg(dx11)] - dx11: Hub, + dx11: Hub, #[cfg(gl)] - gl: Hub, + gl: Hub, } impl Hubs { @@ -756,10 +746,9 @@ impl Hubs { } } -#[derive(Debug)] pub struct Global { pub instance: Instance, - pub surfaces: Registry, + pub surfaces: Registry, hubs: Hubs, } @@ -767,15 +756,15 @@ impl Global { pub fn new(name: &str, factory: G, backends: wgt::BackendBit) -> Self { profiling::scope!("new", "Global"); Self { - instance: Instance::new(name, 1, backends), + instance: Instance::new(name, backends), surfaces: Registry::without_backend(&factory, "Surface"), hubs: Hubs::new(&factory), } } - pub fn clear_backend(&self, _dummy: ()) { + pub fn clear_backend(&self, _dummy: ()) { let mut surface_guard = self.surfaces.data.write(); - let hub = B::hub(self); + let hub = A::hub(self); // this is used for tests, which keep the adapter hub.clear(&mut *surface_guard, false); } @@ -818,14 +807,14 @@ impl Drop for Global { } } -pub trait GfxBackend: hal::Backend { +pub trait HalApi: hal::Api { const VARIANT: Backend; fn hub(global: &Global) -> &Hub; fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface; } #[cfg(vulkan)] -impl GfxBackend for backend::Vulkan { +impl HalApi for hal::api::Vulkan { const VARIANT: Backend = Backend::Vulkan; fn hub(global: &Global) -> &Hub { &global.hubs.vulkan @@ -836,7 +825,7 @@ impl GfxBackend for backend::Vulkan { } #[cfg(metal)] -impl GfxBackend for backend::Metal { +impl HalApi for hal::api::Metal { const VARIANT: Backend = Backend::Metal; fn hub(global: &Global) -> &Hub { &global.hubs.metal @@ -846,8 +835,9 @@ impl GfxBackend for backend::Metal { } } +/* #[cfg(dx12)] -impl GfxBackend for backend::Dx12 { +impl HalApi for hal::api::Dx12 { const VARIANT: Backend = Backend::Dx12; fn hub(global: &Global) -> &Hub { &global.hubs.dx12 @@ -858,7 +848,7 @@ impl GfxBackend for backend::Dx12 { } #[cfg(dx11)] -impl GfxBackend for backend::Dx11 { +impl HalApi for hal::api::Dx11 { const VARIANT: Backend = Backend::Dx11; fn hub(global: &Global) -> &Hub { &global.hubs.dx11 @@ -869,7 +859,7 @@ impl GfxBackend for backend::Dx11 { } #[cfg(gl)] -impl GfxBackend for backend::Gl { +impl HalApi for hal::api::Gles { const VARIANT: Backend = Backend::Gl; fn hub(global: &Global) -> &Hub { &global.hubs.gl @@ -877,7 +867,7 @@ impl GfxBackend for backend::Gl { fn get_surface_mut(surface: &mut Surface) -> &mut Self::Surface { surface.gl.as_mut().unwrap() } -} +}*/ #[cfg(test)] fn _test_send_sync(global: &Global) { diff --git a/wgpu-core/src/id.rs b/wgpu-core/src/id.rs index 9578a77b51..b9d5497758 100644 --- a/wgpu-core/src/id.rs +++ b/wgpu-core/src/id.rs @@ -8,7 +8,7 @@ use wgt::Backend; const BACKEND_BITS: usize = 3; const EPOCH_MASK: u32 = (1 << (32 - BACKEND_BITS)) - 1; -type Dummy = crate::backend::Empty; +type Dummy = hal::api::Empty; #[repr(transparent)] #[cfg_attr(feature = "trace", derive(serde::Serialize), serde(into = "SerialId"))] diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 882fcfa48d..b793b3d871 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -3,64 +3,67 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - backend, conv, device::{Device, DeviceDescriptor}, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Input, Token}, id::{AdapterId, DeviceId, SurfaceId, Valid}, - LabelHelpers, LifeGuard, PrivateFeatures, Stored, DOWNLEVEL_WARNING_MESSAGE, MAX_BIND_GROUPS, + LabelHelpers, LifeGuard, Stored, DOWNLEVEL_WARNING_MESSAGE, }; use wgt::{Backend, BackendBit, PowerPreference, BIND_BUFFER_ALIGNMENT}; -use hal::{ - adapter::PhysicalDevice as _, queue::QueueFamily as _, window::Surface as _, Instance as _, -}; +use hal::{Adapter as _, Instance as _}; use thiserror::Error; -/// Size that is guaranteed to be available in push constants. -/// -/// This is needed because non-vulkan backends might not -/// provide a push-constant size limit. -const MIN_PUSH_CONSTANT_SIZE: u32 = 128; - pub type RequestAdapterOptions = wgt::RequestAdapterOptions; +type HalInstance = ::Instance; +type HalSurface = ::Surface; -#[derive(Debug)] pub struct Instance { + #[allow(dead_code)] + name: String, #[cfg(vulkan)] - pub vulkan: Option, + pub vulkan: Option>, #[cfg(metal)] - pub metal: Option, + pub metal: Option>, #[cfg(dx12)] - pub dx12: Option, + pub dx12: Option>, #[cfg(dx11)] - pub dx11: Option, + pub dx11: Option>, #[cfg(gl)] - pub gl: Option, + pub gl: Option>, } impl Instance { - pub fn new(name: &str, version: u32, backends: BackendBit) -> Self { - backends_map! { - let map = |(backend, backend_create)| { - if backends.contains(backend.into()) { - backend_create(name, version).ok() - } else { - None - } - }; - Self { - #[cfg(vulkan)] - vulkan: map((Backend::Vulkan, gfx_backend_vulkan::Instance::create)), - #[cfg(metal)] - metal: map((Backend::Metal, gfx_backend_metal::Instance::create)), - #[cfg(dx12)] - dx12: map((Backend::Dx12, gfx_backend_dx12::Instance::create)), - #[cfg(dx11)] - dx11: map((Backend::Dx11, gfx_backend_dx11::Instance::create)), - #[cfg(gl)] - gl: map((Backend::Gl, gfx_backend_gl::Instance::create)), + pub fn new(name: &str, backends: BackendBit) -> Self { + let mut flags = hal::InstanceFlag::empty(); + if cfg!(debug_assertions) { + flags |= hal::InstanceFlag::VALIDATION; + flags |= hal::InstanceFlag::DEBUG; + } + let hal_desc = hal::InstanceDescriptor { + name: "wgpu", + flags, + }; + + let map = |backend: Backend| unsafe { + if backends.contains(backend.into()) { + hal::Instance::init(&hal_desc).ok() + } else { + None } + }; + Self { + name: name.to_string(), + #[cfg(vulkan)] + vulkan: map(Backend::Vulkan), + #[cfg(metal)] + metal: map(Backend::Metal), + #[cfg(dx12)] + dx12: map(Backend::Dx12), + #[cfg(dx11)] + dx11: map(Backend::Dx11), + #[cfg(gl)] + gl: map(Backend::Gl), } } @@ -88,20 +91,17 @@ impl Instance { } } -type GfxSurface = ::Surface; - -#[derive(Debug)] pub struct Surface { #[cfg(vulkan)] - pub vulkan: Option>, + pub vulkan: Option>, #[cfg(metal)] - pub metal: Option>, + pub metal: Option>, #[cfg(dx12)] - pub dx12: Option>, + pub dx12: Option>, #[cfg(dx11)] - pub dx11: Option>, + pub dx11: Option>, #[cfg(gl)] - pub gl: Option>, + pub gl: Option>, } impl crate::hub::Resource for Surface { @@ -116,236 +116,15 @@ impl crate::hub::Resource for Surface { } } -const FEATURE_MAP: &[(wgt::Features, hal::Features)] = &[ - (wgt::Features::DEPTH_CLAMPING, hal::Features::DEPTH_CLAMP), - ( - wgt::Features::TEXTURE_COMPRESSION_BC, - hal::Features::FORMAT_BC, - ), - ( - wgt::Features::TEXTURE_COMPRESSION_ETC2, - hal::Features::FORMAT_ETC2, - ), - ( - wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR, - hal::Features::FORMAT_ASTC_LDR, - ), - ( - wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY, - hal::Features::TEXTURE_DESCRIPTOR_ARRAY, - ), - ( - wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, - hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING, - ), - ( - wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, - hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING, - ), - ( - wgt::Features::UNSIZED_BINDING_ARRAY, - hal::Features::UNSIZED_DESCRIPTOR_ARRAY, - ), - ( - wgt::Features::MULTI_DRAW_INDIRECT, - hal::Features::MULTI_DRAW_INDIRECT, - ), - ( - wgt::Features::MULTI_DRAW_INDIRECT_COUNT, - hal::Features::DRAW_INDIRECT_COUNT, - ), - ( - wgt::Features::NON_FILL_POLYGON_MODE, - hal::Features::NON_FILL_POLYGON_MODE, - ), - ( - wgt::Features::PIPELINE_STATISTICS_QUERY, - hal::Features::PIPELINE_STATISTICS_QUERY, - ), - (wgt::Features::SHADER_FLOAT64, hal::Features::SHADER_FLOAT64), - ( - wgt::Features::CONSERVATIVE_RASTERIZATION, - hal::Features::CONSERVATIVE_RASTERIZATION, - ), - ( - wgt::Features::BUFFER_BINDING_ARRAY, - hal::Features::BUFFER_DESCRIPTOR_ARRAY, - ), - ( - wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, - hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, - ), - ( - wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING, - ), - ( - wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, - hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, - ), - ( - wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING, - ), - ( - wgt::Features::VERTEX_WRITABLE_STORAGE, - hal::Features::VERTEX_STORES_AND_ATOMICS, - ), - ( - wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, - hal::Features::SAMPLER_BORDER_COLOR, - ), -]; - -#[derive(Debug)] -pub struct Adapter { - pub(crate) raw: hal::adapter::Adapter, - features: wgt::Features, - pub(crate) private_features: PrivateFeatures, - limits: wgt::Limits, - downlevel: wgt::DownlevelProperties, +pub struct Adapter { + pub(crate) raw: hal::ExposedAdapter, life_guard: LifeGuard, } -impl Adapter { - fn new(raw: hal::adapter::Adapter) -> Self { - profiling::scope!("new", "Adapter"); - - let adapter_features = raw.physical_device.features(); - let properties = raw.physical_device.properties(); - - let mut features = wgt::Features::default() - | wgt::Features::MAPPABLE_PRIMARY_BUFFERS - | wgt::Features::PUSH_CONSTANTS - | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES - | wgt::Features::CLEAR_COMMANDS; - for &(hi, lo) in FEATURE_MAP.iter() { - features.set(hi, adapter_features.contains(lo)); - } - features.set( - wgt::Features::TIMESTAMP_QUERY, - properties.limits.timestamp_compute_and_graphics, - ); - - let private_features = PrivateFeatures { - anisotropic_filtering: adapter_features.contains(hal::Features::SAMPLER_ANISOTROPY), - texture_d24: raw - .physical_device - .format_properties(Some(hal::format::Format::X8D24Unorm)) - .optimal_tiling - .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT), - texture_d24_s8: raw - .physical_device - .format_properties(Some(hal::format::Format::D24UnormS8Uint)) - .optimal_tiling - .contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT), - }; - - let default_limits = wgt::Limits::default(); - - // All these casts to u32 are safe as the underlying vulkan types are u32s. - // If another backend provides larger limits than u32, we need to clamp them to u32::MAX. - // TODO: fix all gfx-hal backends to produce limits we care about, and remove .max - let desc_limits = &properties.limits.descriptor_limits; - let limits = wgt::Limits { - max_texture_dimension_1d: properties - .limits - .max_image_1d_size - .max(default_limits.max_texture_dimension_1d), - max_texture_dimension_2d: properties - .limits - .max_image_2d_size - .max(default_limits.max_texture_dimension_1d), - max_texture_dimension_3d: properties - .limits - .max_image_3d_size - .max(default_limits.max_texture_dimension_1d), - max_texture_array_layers: (properties.limits.max_image_array_layers as u32) - .max(default_limits.max_texture_array_layers), - max_bind_groups: (properties.limits.max_bound_descriptor_sets as u32) - .min(MAX_BIND_GROUPS as u32) - .max(default_limits.max_bind_groups), - max_dynamic_uniform_buffers_per_pipeline_layout: desc_limits - .max_descriptor_set_uniform_buffers_dynamic - .max(default_limits.max_dynamic_uniform_buffers_per_pipeline_layout), - max_dynamic_storage_buffers_per_pipeline_layout: desc_limits - .max_descriptor_set_storage_buffers_dynamic - .max(default_limits.max_dynamic_storage_buffers_per_pipeline_layout), - max_sampled_textures_per_shader_stage: desc_limits - .max_per_stage_descriptor_sampled_images - .max(default_limits.max_sampled_textures_per_shader_stage), - max_samplers_per_shader_stage: desc_limits - .max_per_stage_descriptor_samplers - .max(default_limits.max_samplers_per_shader_stage), - max_storage_buffers_per_shader_stage: desc_limits - .max_per_stage_descriptor_storage_buffers - .max(default_limits.max_storage_buffers_per_shader_stage), - max_storage_textures_per_shader_stage: desc_limits - .max_per_stage_descriptor_storage_images - .max(default_limits.max_storage_textures_per_shader_stage), - max_uniform_buffers_per_shader_stage: desc_limits - .max_per_stage_descriptor_uniform_buffers - .max(default_limits.max_uniform_buffers_per_shader_stage), - max_uniform_buffer_binding_size: (properties.limits.max_uniform_buffer_range as u32) - .max(default_limits.max_uniform_buffer_binding_size), - max_storage_buffer_binding_size: (properties.limits.max_storage_buffer_range as u32) - .max(default_limits.max_storage_buffer_binding_size), - max_vertex_buffers: (properties.limits.max_vertex_input_bindings as u32) - .max(default_limits.max_vertex_buffers), - max_vertex_attributes: (properties.limits.max_vertex_input_attributes as u32) - .max(default_limits.max_vertex_attributes), - max_vertex_buffer_array_stride: (properties.limits.max_vertex_input_binding_stride - as u32) - .max(default_limits.max_vertex_buffer_array_stride), - max_push_constant_size: (properties.limits.max_push_constants_size as u32) - .max(MIN_PUSH_CONSTANT_SIZE), // As an extension, the default is always 0, so define a separate minimum. - }; - - let mut downlevel_flags = wgt::DownlevelFlags::empty(); - downlevel_flags.set( - wgt::DownlevelFlags::COMPUTE_SHADERS, - properties.downlevel.compute_shaders, - ); - downlevel_flags.set( - wgt::DownlevelFlags::STORAGE_IMAGES, - properties.downlevel.storage_images, - ); - downlevel_flags.set( - wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL, - properties.downlevel.read_only_depth_stencil, - ); - downlevel_flags.set( - wgt::DownlevelFlags::DEVICE_LOCAL_IMAGE_COPIES, - properties.downlevel.device_local_image_copies, - ); - downlevel_flags.set( - wgt::DownlevelFlags::NON_POWER_OF_TWO_MIPMAPPED_TEXTURES, - properties.downlevel.non_power_of_two_mipmapped_textures, - ); - downlevel_flags.set( - wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES, - adapter_features.contains(hal::Features::IMAGE_CUBE_ARRAY), - ); - downlevel_flags.set( - wgt::DownlevelFlags::ANISOTROPIC_FILTERING, - private_features.anisotropic_filtering, - ); - - let downlevel = wgt::DownlevelProperties { - flags: downlevel_flags, - shader_model: match properties.downlevel.shader_model { - hal::DownlevelShaderModel::ShaderModel2 => wgt::ShaderModel::Sm2, - hal::DownlevelShaderModel::ShaderModel4 => wgt::ShaderModel::Sm4, - hal::DownlevelShaderModel::ShaderModel5 => wgt::ShaderModel::Sm5, - }, - }; - +impl Adapter { + fn new(raw: hal::ExposedAdapter) -> Self { Self { raw, - features, - private_features, - limits, - downlevel, life_guard: LifeGuard::new(""), } } @@ -354,75 +133,64 @@ impl Adapter { &self, surface: &mut Surface, ) -> Result { - let formats = { - let surface = B::get_surface_mut(surface); - let queue_family = &self.raw.queue_families[0]; - if !surface.supports_queue_family(queue_family) { - return Err(GetSwapChainPreferredFormatError::UnsupportedQueueFamily); - } - surface.supported_formats(&self.raw.physical_device) + // Check the four formats mentioned in the WebGPU spec. + // Also, prefer sRGB over linear as it is better in + // representing perceived colors. + let preferred_formats = [ + wgt::TextureFormat::Bgra8UnormSrgb, + wgt::TextureFormat::Rgba8UnormSrgb, + wgt::TextureFormat::Bgra8Unorm, + wgt::TextureFormat::Rgba8Unorm, + ]; + + let caps = unsafe { + self.raw + .adapter + .surface_capabilities(A::get_surface_mut(surface)) + .ok_or(GetSwapChainPreferredFormatError::UnsupportedQueueFamily)? }; - if let Some(formats) = formats { - // Check the four formats mentioned in the WebGPU spec: - // Bgra8UnormSrgb, Rgba8UnormSrgb, Bgra8Unorm, Rgba8Unorm - // Also, prefer sRGB over linear as it is better in - // representing perceived colors. - if formats.contains(&hal::format::Format::Bgra8Srgb) { - return Ok(wgt::TextureFormat::Bgra8UnormSrgb); - } - if formats.contains(&hal::format::Format::Rgba8Srgb) { - return Ok(wgt::TextureFormat::Rgba8UnormSrgb); - } - if formats.contains(&hal::format::Format::Bgra8Unorm) { - return Ok(wgt::TextureFormat::Bgra8Unorm); - } - if formats.contains(&hal::format::Format::Rgba8Unorm) { - return Ok(wgt::TextureFormat::Rgba8Unorm); - } - return Err(GetSwapChainPreferredFormatError::NotFound); - } - // If no formats were returned, use Bgra8UnormSrgb - Ok(wgt::TextureFormat::Bgra8UnormSrgb) + preferred_formats + .iter() + .cloned() + .find(|preferred| caps.formats.contains(preferred)) + .ok_or(GetSwapChainPreferredFormatError::NotFound) } pub(crate) fn get_texture_format_features( &self, format: wgt::TextureFormat, ) -> wgt::TextureFormatFeatures { - let texture_format_properties = self - .raw - .physical_device - .format_properties(Some(conv::map_texture_format( - format, - self.private_features, - ))) - .optimal_tiling; + let caps = unsafe { self.raw.adapter.texture_format_capabilities(format) }; let mut allowed_usages = format.describe().guaranteed_format_features.allowed_usages; - if texture_format_properties.contains(hal::format::ImageFeature::SAMPLED) { - allowed_usages |= wgt::TextureUsage::SAMPLED; - } - if texture_format_properties.contains(hal::format::ImageFeature::STORAGE) { - allowed_usages |= wgt::TextureUsage::STORAGE; - } - if texture_format_properties.contains(hal::format::ImageFeature::COLOR_ATTACHMENT) { - allowed_usages |= wgt::TextureUsage::RENDER_ATTACHMENT; - } - if texture_format_properties.contains(hal::format::ImageFeature::DEPTH_STENCIL_ATTACHMENT) { - allowed_usages |= wgt::TextureUsage::RENDER_ATTACHMENT; - } + allowed_usages.set( + wgt::TextureUsage::SAMPLED, + caps.contains(hal::TextureFormatCapability::SAMPLED), + ); + allowed_usages.set( + wgt::TextureUsage::STORAGE, + caps.contains(hal::TextureFormatCapability::STORAGE), + ); + allowed_usages.set( + wgt::TextureUsage::RENDER_ATTACHMENT, + caps.intersects( + hal::TextureFormatCapability::COLOR_ATTACHMENT + | hal::TextureFormatCapability::DEPTH_STENCIL_ATTACHMENT, + ), + ); let mut flags = wgt::TextureFormatFeatureFlags::empty(); - if texture_format_properties.contains(hal::format::ImageFeature::STORAGE_ATOMIC) { - flags |= wgt::TextureFormatFeatureFlags::STORAGE_ATOMICS; - } - if texture_format_properties.contains(hal::format::ImageFeature::STORAGE_READ_WRITE) { - flags |= wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE; - } + flags.set( + wgt::TextureFormatFeatureFlags::STORAGE_ATOMICS, + caps.contains(hal::TextureFormatCapability::STORAGE_ATOMIC), + ); + flags.set( + wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE, + caps.contains(hal::TextureFormatCapability::STORAGE_READ_WRITE), + ); - let filterable = - texture_format_properties.contains(hal::format::ImageFeature::SAMPLED_LINEAR); + let filterable = caps.contains(hal::TextureFormatCapability::SAMPLED_LINEAR); wgt::TextureFormatFeatures { allowed_usages, @@ -436,15 +204,16 @@ impl Adapter { self_id: AdapterId, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, - ) -> Result, RequestDeviceError> { + ) -> Result, RequestDeviceError> { // Verify all features were exposed by the adapter - if !self.features.contains(desc.features) { + if !self.raw.features.contains(desc.features) { return Err(RequestDeviceError::UnsupportedFeature( - desc.features - self.features, + desc.features - self.raw.features, )); } - if !self.downlevel.is_webgpu_compliant() { + let caps = &self.raw.capabilities; + if !caps.downlevel.is_webgpu_compliant() { log::warn!("{}", DOWNLEVEL_WARNING_MESSAGE); } @@ -452,85 +221,42 @@ impl Adapter { if desc .features .contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS) - && self.raw.info.device_type == hal::adapter::DeviceType::DiscreteGpu + && self.raw.info.device_type == wgt::DeviceType::DiscreteGpu { log::warn!("Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. This is a massive performance footgun and likely not what you wanted"); } - let phd = &self.raw.physical_device; - let available_features = phd.features(); - - // Check features that are always needed - let wishful_features = hal::Features::ROBUST_BUFFER_ACCESS - | hal::Features::FRAGMENT_STORES_AND_ATOMICS - | hal::Features::NDC_Y_UP - | hal::Features::INDEPENDENT_BLENDING - | hal::Features::SAMPLER_ANISOTROPY - | hal::Features::IMAGE_CUBE_ARRAY - | hal::Features::SAMPLE_RATE_SHADING; - let mut enabled_features = available_features & wishful_features; - if enabled_features != wishful_features { - log::warn!( - "Missing internal features: {:?}", - wishful_features - enabled_features - ); - } - - // Enable low-level features - for &(hi, lo) in FEATURE_MAP.iter() { - enabled_features.set(lo, desc.features.contains(hi)); - } - - let family = self - .raw - .queue_families - .iter() - .find(|family| family.queue_type().supports_graphics()) - .ok_or(RequestDeviceError::NoGraphicsQueue)?; - - let mut gpu = - unsafe { phd.open(&[(family, &[1.0])], enabled_features) }.map_err(|err| { - use hal::device::CreationError::*; - match err { - DeviceLost => RequestDeviceError::DeviceLost, - InitializationFailed => RequestDeviceError::Internal, - OutOfMemory(_) => RequestDeviceError::OutOfMemory, - _ => panic!("failed to create `gfx-hal` device: {}", err), - } - })?; + let gpu = unsafe { self.raw.adapter.open(desc.features) }.map_err(|err| match err { + hal::DeviceError::Lost => RequestDeviceError::DeviceLost, + hal::DeviceError::OutOfMemory => RequestDeviceError::OutOfMemory, + })?; if let Some(_) = desc.label { //TODO } - let limits = phd.properties().limits; assert_eq!( 0, - BIND_BUFFER_ALIGNMENT % limits.min_storage_buffer_offset_alignment, + BIND_BUFFER_ALIGNMENT % caps.alignments.storage_buffer_offset, "Adapter storage buffer offset alignment not compatible with WGPU" ); assert_eq!( 0, - BIND_BUFFER_ALIGNMENT % limits.min_uniform_buffer_offset_alignment, + BIND_BUFFER_ALIGNMENT % caps.alignments.uniform_buffer_offset, "Adapter uniform buffer offset alignment not compatible with WGPU" ); - if self.limits < desc.limits { + if caps.limits < desc.limits { return Err(RequestDeviceError::LimitsExceeded); } - let mem_props = phd.memory_properties(); - Device::new( - gpu.device, + gpu, Stored { value: Valid(self_id), ref_count: self.life_guard.add_ref(), }, - gpu.queue_groups.swap_remove(0), - mem_props, - limits, - self.private_features, - self.downlevel, + caps.alignments.clone(), + caps.downlevel, desc, trace_path, ) @@ -538,7 +264,7 @@ impl Adapter { } } -impl crate::hub::Resource for Adapter { +impl crate::hub::Resource for Adapter { const TYPE: &'static str = "Adapter"; fn life_guard(&self) -> &LifeGuard { @@ -648,6 +374,7 @@ impl Global { id.0 } + /*TODO: raw CALayer handling #[cfg(metal)] pub fn instance_create_surface_metal( &self, @@ -667,7 +394,7 @@ impl Global { let mut token = Token::root(); let id = self.surfaces.prepare(id_in).assign(surface, &mut token); id.0 - } + }*/ pub fn surface_drop(&self, id: SurfaceId) { profiling::scope!("drop", "Surface"); @@ -684,11 +411,11 @@ impl Global { let mut adapters = Vec::new(); backends_map! { - let map = |(instance_field, backend, backend_info, backend_hub)| { + let map = |(instance_field, backend, backend_info)| { if let Some(ref inst) = *instance_field { - let hub = backend_hub(self); + let hub = HalApi::hub(self); if let Some(id_backend) = inputs.find(backend) { - for raw in inst.enumerate_adapters() { + for raw in unsafe {inst.enumerate_adapters()} { let adapter = Adapter::new(raw); log::info!("Adapter {} {:?}", backend_info, adapter.raw.info); let id = hub.adapters @@ -701,15 +428,15 @@ impl Global { }; #[cfg(vulkan)] - map((&instance.vulkan, Backend::Vulkan, "Vulkan", backend::Vulkan::hub)), + map((&instance.vulkan, Backend::Vulkan, "Vulkan")), #[cfg(metal)] - map((&instance.metal, Backend::Metal, "Metal", backend::Metal::hub)), + map((&instance.metal, Backend::Metal, "Metal")), #[cfg(dx12)] - map((&instance.dx12, Backend::Dx12, "Dx12", backend::Dx12::hub)), + map((&instance.dx12, Backend::Dx12, "Dx12")), #[cfg(dx11)] - map((&instance.dx11, Backend::Dx11, "Dx11", backend::Dx11::hub)), + map((&instance.dx11, Backend::Dx11, "Dx11")), #[cfg(gl)] - map((&instance.gl, Backend::Gl, "GL", backend::Gl::hub)), + map((&instance.gl, Backend::Gl, "GL")), } adapters @@ -745,16 +472,13 @@ impl Global { let map = |(instance_backend, id_backend, surface_backend)| { match *instance_backend { Some(ref inst) if id_backend.is_some() => { - let mut adapters = inst.enumerate_adapters(); + let mut adapters = unsafe { inst.enumerate_adapters() }; if let Some(surface_backend) = compatible_surface.and_then(surface_backend) { - adapters.retain(|a| { - a.queue_families - .iter() - .find(|qf| qf.queue_type().supports_graphics()) - .map_or(false, |qf| surface_backend.supports_queue_family(qf)) + adapters.retain(|exposed| unsafe { + exposed.adapter.surface_capabilities(surface_backend).is_some() }); } - device_types.extend(adapters.iter().map(|ad| ad.info.device_type.clone())); + device_types.extend(adapters.iter().map(|ad| ad.info.device_type)); adapters } _ => Vec::new(), @@ -765,35 +489,35 @@ impl Global { // being weird with lifetimes for closure literals... #[cfg(vulkan)] let adapters_vk = map((&instance.vulkan, &id_vulkan, { - fn surface_vulkan(surf: &Surface) -> Option<&GfxSurface> { + fn surface_vulkan(surf: &Surface) -> Option<&HalSurface> { surf.vulkan.as_ref() } surface_vulkan })); #[cfg(metal)] let adapters_mtl = map((&instance.metal, &id_metal, { - fn surface_metal(surf: &Surface) -> Option<&GfxSurface> { + fn surface_metal(surf: &Surface) -> Option<&HalSurface> { surf.metal.as_ref() } surface_metal })); #[cfg(dx12)] let adapters_dx12 = map((&instance.dx12, &id_dx12, { - fn surface_dx12(surf: &Surface) -> Option<&GfxSurface> { + fn surface_dx12(surf: &Surface) -> Option<&HalSurface> { surf.dx12.as_ref() } surface_dx12 })); #[cfg(dx11)] let adapters_dx11 = map((&instance.dx11, &id_dx11, { - fn surface_dx11(surf: &Surface) -> Option<&GfxSurface> { + fn surface_dx11(surf: &Surface) -> Option<&HalSurface> { surf.dx11.as_ref() } surface_dx11 })); #[cfg(gl)] let adapters_gl = map((&instance.gl, &id_gl, { - fn surface_gl(surf: &Surface) -> Option<&GfxSurface> { + fn surface_gl(surf: &Surface) -> Option<&HalSurface> { surf.gl.as_ref() } surface_gl @@ -809,19 +533,19 @@ impl Global { for (i, ty) in device_types.into_iter().enumerate() { match ty { - hal::adapter::DeviceType::IntegratedGpu => { + wgt::DeviceType::IntegratedGpu => { integrated = integrated.or(Some(i)); } - hal::adapter::DeviceType::DiscreteGpu => { + wgt::DeviceType::DiscreteGpu => { discrete = discrete.or(Some(i)); } - hal::adapter::DeviceType::VirtualGpu => { + wgt::DeviceType::VirtualGpu => { virt = virt.or(Some(i)); } - hal::adapter::DeviceType::Cpu => { + wgt::DeviceType::Cpu => { cpu = cpu.or(Some(i)); } - hal::adapter::DeviceType::Other => { + wgt::DeviceType::Other => { other = other.or(Some(i)); } } @@ -835,11 +559,11 @@ impl Global { let mut selected = preferred_gpu.unwrap_or(0); backends_map! { - let map = |(info_adapter, id_backend, mut adapters_backend, backend_hub)| { + let map = |(info_adapter, id_backend, mut adapters_backend)| { if selected < adapters_backend.len() { let adapter = Adapter::new(adapters_backend.swap_remove(selected)); log::info!("Adapter {} {:?}", info_adapter, adapter.raw.info); - let id = backend_hub(self).adapters + let id = HalApi::hub(self).adapters .prepare(id_backend.take().unwrap()) .assign(adapter, &mut token); return Ok(id.0); @@ -848,15 +572,15 @@ impl Global { }; #[cfg(vulkan)] - map(("Vulkan", &mut id_vulkan, adapters_vk, backend::Vulkan::hub)), + map(("Vulkan", &mut id_vulkan, adapters_vk)), #[cfg(metal)] - map(("Metal", &mut id_metal, adapters_mtl, backend::Metal::hub)), + map(("Metal", &mut id_metal, adapters_mtl)), #[cfg(dx12)] - map(("Dx12", &mut id_dx12, adapters_dx12, backend::Dx12::hub)), + map(("Dx12", &mut id_dx12, adapters_dx12)), #[cfg(dx11)] - map(("Dx11", &mut id_dx11, adapters_dx11, backend::Dx11::hub)), + map(("Dx11", &mut id_dx11, adapters_dx11)), #[cfg(gl)] - map(("GL", &mut id_gl, adapters_gl, backend::Gl::hub)), + map(("GL", &mut id_gl, adapters_gl)), } let _ = ( @@ -871,25 +595,25 @@ impl Global { Err(RequestAdapterError::NotFound) } - pub fn adapter_get_info( + pub fn adapter_get_info( &self, adapter_id: AdapterId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (adapter_guard, _) = hub.adapters.read(&mut token); adapter_guard .get(adapter_id) - .map(|adapter| conv::map_adapter_info(adapter.raw.info.clone(), adapter_id.backend())) + .map(|adapter| adapter.raw.info.clone()) .map_err(|_| InvalidAdapter) } - pub fn adapter_get_texture_format_features( + pub fn adapter_get_texture_format_features( &self, adapter_id: AdapterId, format: wgt::TextureFormat, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (adapter_guard, _) = hub.adapters.read(&mut token); adapter_guard @@ -898,49 +622,49 @@ impl Global { .map_err(|_| InvalidAdapter) } - pub fn adapter_features( + pub fn adapter_features( &self, adapter_id: AdapterId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (adapter_guard, _) = hub.adapters.read(&mut token); adapter_guard .get(adapter_id) - .map(|adapter| adapter.features) + .map(|adapter| adapter.raw.features) .map_err(|_| InvalidAdapter) } - pub fn adapter_limits( + pub fn adapter_limits( &self, adapter_id: AdapterId, ) -> Result { - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (adapter_guard, _) = hub.adapters.read(&mut token); adapter_guard .get(adapter_id) - .map(|adapter| adapter.limits.clone()) + .map(|adapter| adapter.raw.capabilities.limits.clone()) .map_err(|_| InvalidAdapter) } - pub fn adapter_downlevel_properties( + pub fn adapter_downlevel_properties( &self, adapter_id: AdapterId, - ) -> Result { - let hub = B::hub(self); + ) -> Result { + let hub = A::hub(self); let mut token = Token::root(); let (adapter_guard, _) = hub.adapters.read(&mut token); adapter_guard .get(adapter_id) - .map(|adapter| adapter.downlevel) + .map(|adapter| adapter.raw.capabilities.downlevel) .map_err(|_| InvalidAdapter) } - pub fn adapter_drop(&self, adapter_id: AdapterId) { + pub fn adapter_drop(&self, adapter_id: AdapterId) { profiling::scope!("drop", "Adapter"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut adapter_guard, _) = hub.adapters.write(&mut token); @@ -956,7 +680,7 @@ impl Global { } impl Global { - pub fn adapter_request_device( + pub fn adapter_request_device( &self, adapter_id: AdapterId, desc: &DeviceDescriptor, @@ -965,7 +689,7 @@ impl Global { ) -> (DeviceId, Option) { profiling::scope!("request_device", "Adapter"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.devices.prepare(id_in); diff --git a/wgpu-core/src/lib.rs b/wgpu-core/src/lib.rs index 01db1471ea..48e08bc2dc 100644 --- a/wgpu-core/src/lib.rs +++ b/wgpu-core/src/lib.rs @@ -2,6 +2,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +/*! This library safely implements WebGPU on native platforms. + * It is designed for integration into browsers, as well as wrapping + * into other language-specific user-friendly libraries. + */ + #![allow( // We use loops for getting early-out of scope without closures. clippy::never_loop, @@ -26,21 +31,6 @@ #[macro_use] mod macros; -pub mod backend { - pub use gfx_backend_empty::Backend as Empty; - - #[cfg(dx11)] - pub use gfx_backend_dx11::Backend as Dx11; - #[cfg(dx12)] - pub use gfx_backend_dx12::Backend as Dx12; - #[cfg(gl)] - pub use gfx_backend_gl::Backend as Gl; - #[cfg(metal)] - pub use gfx_backend_metal::Backend as Metal; - #[cfg(vulkan)] - pub use gfx_backend_vulkan::Backend as Vulkan; -} - pub mod binding_model; pub mod command; mod conv; @@ -55,18 +45,18 @@ pub mod swap_chain; mod track; mod validation; +pub use hal::api; + #[cfg(test)] use loom::sync::atomic; #[cfg(not(test))] use std::sync::atomic; -use atomic::{AtomicUsize, Ordering}; +use atomic::{AtomicU64, AtomicUsize, Ordering}; use std::{borrow::Cow, os::raw::c_char, ptr}; -pub const MAX_BIND_GROUPS: usize = 8; - -type SubmissionIndex = usize; +type SubmissionIndex = hal::FenceValue; type Index = u32; type Epoch = u32; @@ -74,18 +64,15 @@ pub type RawString = *const c_char; pub type Label<'a> = Option>; trait LabelHelpers<'a> { - fn to_string_or_default(&'a self) -> String; + fn borrow_option(&'a self) -> Option<&'a str>; fn borrow_or_default(&'a self) -> &'a str; } impl<'a> LabelHelpers<'a> for Label<'a> { - fn borrow_or_default(&'a self) -> &'a str { - self.as_ref().map(|cow| cow.as_ref()).unwrap_or("") + fn borrow_option(&'a self) -> Option<&'a str> { + self.as_ref().map(|cow| cow.as_ref()) } - fn to_string_or_default(&'a self) -> String { - self.as_ref() - .map(|cow| cow.as_ref()) - .unwrap_or("") - .to_string() + fn borrow_or_default(&'a self) -> &'a str { + self.borrow_option().unwrap_or_default() } } @@ -196,7 +183,7 @@ impl Drop for MultiRefCount { #[derive(Debug)] pub struct LifeGuard { ref_count: Option, - submission_index: AtomicUsize, + submission_index: AtomicU64, #[cfg(debug_assertions)] pub(crate) label: String, } @@ -207,7 +194,7 @@ impl LifeGuard { let bx = Box::new(AtomicUsize::new(1)); Self { ref_count: ptr::NonNull::new(Box::into_raw(bx)).map(RefCount), - submission_index: AtomicUsize::new(0), + submission_index: AtomicU64::new(0), #[cfg(debug_assertions)] label: label.to_string(), } @@ -230,13 +217,6 @@ struct Stored { ref_count: RefCount, } -#[derive(Clone, Copy, Debug)] -struct PrivateFeatures { - anisotropic_filtering: bool, - texture_d24: bool, - texture_d24_s8: bool, -} - const DOWNLEVEL_WARNING_MESSAGE: &str = "The underlying API or device in use does not \ support enough features to be a fully compliant implementation of WebGPU. A subset of the features can still be used. \ If you are running this program on native and not in a browser and wish to limit the features you use to the supported subset, \ @@ -255,16 +235,19 @@ macro_rules! gfx_select { // macro so we must specify their equivalents manually match $id.backend() { #[cfg(all(not(target_arch = "wasm32"), not(target_os = "ios"), not(target_os = "macos")))] - wgt::Backend::Vulkan => $global.$method::<$crate::backend::Vulkan>( $($param),* ), + 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::backend::Metal>( $($param),* ), + wgt::Backend::Metal => $global.$method::<$crate::api::Metal>( $($param),* ), + /* #[cfg(all(not(target_arch = "wasm32"), windows))] wgt::Backend::Dx12 => $global.$method::<$crate::backend::Dx12>( $($param),* ), #[cfg(all(not(target_arch = "wasm32"), windows))] wgt::Backend::Dx11 => $global.$method::<$crate::backend::Dx11>( $($param),* ), #[cfg(any(target_arch = "wasm32", all(unix, not(any(target_os = "ios", target_os = "macos")))))] wgt::Backend::Gl => $global.$method::<$crate::backend::Gl>( $($param),+ ), + */ other => panic!("Unexpected backend {:?}", other), + } }; } @@ -274,9 +257,3 @@ type FastHashMap = std::collections::HashMap>; /// Fast hash set used internally. type FastHashSet = std::collections::HashSet>; - -#[test] -fn test_default_limits() { - let limits = wgt::Limits::default(); - assert!(limits.max_bind_groups <= MAX_BIND_GROUPS as u32); -} diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index 05adbcd355..f853ad0c14 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -12,7 +12,6 @@ use crate::{ use std::borrow::Cow; use thiserror::Error; -#[derive(Debug)] pub enum ShaderModuleSource<'a> { SpirV(Cow<'a, [u32]>), Wgsl(Cow<'a, str>), @@ -24,20 +23,18 @@ pub enum ShaderModuleSource<'a> { #[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub struct ShaderModuleDescriptor<'a> { pub label: Label<'a>, - #[cfg_attr(any(feature = "replay", feature = "trace"), serde(default))] - pub flags: wgt::ShaderFlags, } #[derive(Debug)] -pub struct ShaderModule { - pub(crate) raw: B::ShaderModule, +pub struct ShaderModule { + pub(crate) raw: A::ShaderModule, pub(crate) device_id: Stored, - pub(crate) interface: Option, + pub(crate) interface: validation::Interface, #[cfg(debug_assertions)] pub(crate) label: String, } -impl Resource for ShaderModule { +impl Resource for ShaderModule { const TYPE: &'static str = "ShaderModule"; fn life_guard(&self) -> &LifeGuard { @@ -54,7 +51,7 @@ impl Resource for ShaderModule { #[derive(Clone, Debug, Error)] pub enum CreateShaderModuleError { - #[error("Failed to parse WGSL")] + #[error("Failed to parse a shader")] Parsing, #[error("Failed to generate the backend-specific code")] Generation, @@ -125,14 +122,14 @@ pub enum CreateComputePipelineError { } #[derive(Debug)] -pub struct ComputePipeline { - pub(crate) raw: B::ComputePipeline, +pub struct ComputePipeline { + pub(crate) raw: A::ComputePipeline, pub(crate) layout_id: Stored, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, } -impl Resource for ComputePipeline { +impl Resource for ComputePipeline { const TYPE: &'static str = "ComputePipeline"; fn life_guard(&self) -> &LifeGuard { @@ -289,8 +286,8 @@ bitflags::bitflags! { } #[derive(Debug)] -pub struct RenderPipeline { - pub(crate) raw: B::GraphicsPipeline, +pub struct RenderPipeline { + pub(crate) raw: A::RenderPipeline, pub(crate) layout_id: Stored, pub(crate) device_id: Stored, pub(crate) pass_context: RenderPassContext, @@ -300,7 +297,7 @@ pub struct RenderPipeline { pub(crate) life_guard: LifeGuard, } -impl Resource for RenderPipeline { +impl Resource for RenderPipeline { const TYPE: &'static str = "RenderPipeline"; fn life_guard(&self) -> &LifeGuard { diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 171ae4bac7..9ee45db1f9 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - device::{alloc::MemoryBlock, DeviceError, HostMap, MissingFeatures}, + device::{DeviceError, HostMap, MissingFeatures}, hub::Resource, id::{DeviceId, SwapChainId, TextureId}, memory_init_tracker::MemoryInitTracker, @@ -16,56 +16,6 @@ use thiserror::Error; use std::{borrow::Borrow, num::NonZeroU8, ops::Range, ptr::NonNull}; -bitflags::bitflags! { - /// The internal enum mirrored from `BufferUsage`. The values don't have to match! - pub struct BufferUse: u32 { - const EMPTY = 0; - const MAP_READ = 1; - const MAP_WRITE = 2; - const COPY_SRC = 4; - const COPY_DST = 8; - const INDEX = 16; - const VERTEX = 32; - const UNIFORM = 64; - const STORAGE_LOAD = 128; - const STORAGE_STORE = 256; - const INDIRECT = 512; - /// The combination of all read-only usages. - const READ_ALL = Self::MAP_READ.bits | Self::COPY_SRC.bits | - Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | - Self::STORAGE_LOAD.bits | Self::INDIRECT.bits; - /// The combination of all write-only and read-write usages. - const WRITE_ALL = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_STORE.bits; - /// The combination of all usages that the are guaranteed to be be ordered by the hardware. - /// If a usage is not ordered, then even if it doesn't change between draw calls, there - /// still need to be pipeline barriers inserted for synchronization. - const ORDERED = Self::READ_ALL.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits; - } -} - -bitflags::bitflags! { - /// The internal enum mirrored from `TextureUsage`. The values don't have to match! - pub struct TextureUse: u32 { - const EMPTY = 0; - const COPY_SRC = 1; - const COPY_DST = 2; - const SAMPLED = 4; - const ATTACHMENT_READ = 8; - const ATTACHMENT_WRITE = 16; - const STORAGE_LOAD = 32; - const STORAGE_STORE = 48; - /// The combination of all read-only usages. - const READ_ALL = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::ATTACHMENT_READ.bits | Self::STORAGE_LOAD.bits; - /// The combination of all write-only and read-write usages. - const WRITE_ALL = Self::COPY_DST.bits | Self::ATTACHMENT_WRITE.bits | Self::STORAGE_STORE.bits; - /// The combination of all usages that the are guaranteed to be be ordered by the hardware. - /// If a usage is not ordered, then even if it doesn't change between draw calls, there - /// still need to be pipeline barriers inserted for synchronization. - const ORDERED = Self::READ_ALL.bits | Self::COPY_DST.bits | Self::ATTACHMENT_WRITE.bits; - const UNINITIALIZED = 0xFFFF; - } -} - #[repr(C)] #[derive(Debug)] pub enum BufferMapAsyncStatus { @@ -77,12 +27,11 @@ pub enum BufferMapAsyncStatus { } #[derive(Debug)] -pub(crate) enum BufferMapState { +pub(crate) enum BufferMapState { /// Mapped at creation. Init { ptr: NonNull, - stage_buffer: B::Buffer, - stage_memory: MemoryBlock, + stage_buffer: A::Buffer, needs_flush: bool, }, /// Waiting for GPU to be done before mapping @@ -90,15 +39,15 @@ pub(crate) enum BufferMapState { /// Mapped Active { ptr: NonNull, - sub_range: hal::buffer::SubRange, + range: hal::MemoryRange, host: HostMap, }, /// Not mapped Idle, } -unsafe impl Send for BufferMapState {} -unsafe impl Sync for BufferMapState {} +unsafe impl Send for BufferMapState {} +unsafe impl Sync for BufferMapState {} pub type BufferMapCallback = unsafe extern "C" fn(status: BufferMapAsyncStatus, userdata: *mut u8); @@ -170,15 +119,15 @@ pub(crate) struct BufferPendingMapping { pub type BufferDescriptor<'a> = wgt::BufferDescriptor>; #[derive(Debug)] -pub struct Buffer { - pub(crate) raw: Option<(B::Buffer, MemoryBlock)>, +pub struct Buffer { + pub(crate) raw: Option, pub(crate) device_id: Stored, pub(crate) usage: wgt::BufferUsage, pub(crate) size: wgt::BufferAddress, pub(crate) initialization_status: MemoryInitTracker, - pub(crate) sync_mapped_writes: Option, + pub(crate) sync_mapped_writes: Option, pub(crate) life_guard: LifeGuard, - pub(crate) map_state: BufferMapState, + pub(crate) map_state: BufferMapState, } #[derive(Clone, Debug, Error)] @@ -195,7 +144,7 @@ pub enum CreateBufferError { UsageMismatch(wgt::BufferUsage), } -impl Resource for Buffer { +impl Resource for Buffer { const TYPE: &'static str = "Buffer"; fn life_guard(&self) -> &LifeGuard { @@ -203,7 +152,7 @@ impl Resource for Buffer { } } -impl Borrow<()> for Buffer { +impl Borrow<()> for Buffer { fn borrow(&self) -> &() { &DUMMY_SELECTOR } @@ -212,16 +161,12 @@ impl Borrow<()> for Buffer { pub type TextureDescriptor<'a> = wgt::TextureDescriptor>; #[derive(Debug)] -pub struct Texture { - pub(crate) raw: Option<(B::Image, MemoryBlock)>, +pub struct Texture { + pub(crate) raw: Option, pub(crate) device_id: Stored, - pub(crate) usage: wgt::TextureUsage, - pub(crate) aspects: hal::format::Aspects, - pub(crate) dimension: wgt::TextureDimension, - pub(crate) kind: hal::image::Kind, - pub(crate) format: wgt::TextureFormat, + pub(crate) desc: wgt::TextureDescriptor<()>, + pub(crate) hal_usage: hal::TextureUse, pub(crate) format_features: wgt::TextureFormatFeatures, - pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment, pub(crate) full_range: TextureSelector, pub(crate) life_guard: LifeGuard, } @@ -265,7 +210,7 @@ pub enum CreateTextureError { MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures), } -impl Resource for Texture { +impl Resource for Texture { const TYPE: &'static str = "Texture"; fn life_guard(&self) -> &LifeGuard { @@ -273,7 +218,7 @@ impl Resource for Texture { } } -impl Borrow for Texture { +impl Borrow for Texture { fn borrow(&self) -> &TextureSelector { &self.full_range } @@ -297,30 +242,35 @@ pub struct TextureViewDescriptor<'a> { } #[derive(Debug)] -pub(crate) enum TextureViewInner { - Native { - raw: B::ImageView, - source_id: Stored, - }, - SwapChain { - image: >::SwapchainImage, - source_id: Stored, - }, +pub(crate) enum TextureViewSource { + Native(Stored), + SwapChain(Stored), } #[derive(Debug)] -pub struct TextureView { - pub(crate) inner: TextureViewInner, +pub(crate) struct HalTextureViewDescriptor { + pub format: wgt::TextureFormat, + pub dimension: wgt::TextureViewDimension, + pub range: wgt::ImageSubresourceRange, +} + +impl HalTextureViewDescriptor { + pub fn aspects(&self) -> hal::FormatAspect { + hal::FormatAspect::from(self.format) & hal::FormatAspect::from(self.range.aspect) + } +} + +#[derive(Debug)] +pub struct TextureView { + pub(crate) raw: A::TextureView, + pub(crate) source: TextureViewSource, //TODO: store device_id for quick access? - pub(crate) aspects: hal::format::Aspects, - pub(crate) format: wgt::TextureFormat, + pub(crate) desc: HalTextureViewDescriptor, pub(crate) format_features: wgt::TextureFormatFeatures, - pub(crate) dimension: wgt::TextureViewDimension, pub(crate) extent: wgt::Extent3d, - pub(crate) samples: hal::image::NumSamples, - pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment, + pub(crate) samples: u32, /// Internal use of this texture view when used as `BindingType::Texture`. - pub(crate) sampled_internal_use: TextureUse, + pub(crate) sampled_internal_use: hal::TextureUse, pub(crate) selector: TextureSelector, pub(crate) life_guard: LifeGuard, } @@ -331,30 +281,35 @@ pub enum CreateTextureViewError { InvalidTexture, #[error("not enough memory left")] OutOfMemory, - #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{image:?}`")] + #[error("Invalid texture view dimension `{view:?}` with texture of dimension `{texture:?}`")] InvalidTextureViewDimension { view: wgt::TextureViewDimension, - image: wgt::TextureDimension, + texture: wgt::TextureDimension, }, #[error("Invalid texture depth `{depth}` for texture view of dimension `Cubemap`. Cubemap views must use images of size 6.")] - InvalidCubemapTextureDepth { depth: u16 }, + InvalidCubemapTextureDepth { depth: u32 }, #[error("Invalid texture depth `{depth}` for texture view of dimension `CubemapArray`. Cubemap views must use images with sizes which are a multiple of 6.")] - InvalidCubemapArrayTextureDepth { depth: u16 }, + InvalidCubemapArrayTextureDepth { depth: u32 }, #[error( "TextureView mip level count + base mip level {requested} must be <= Texture mip level count {total}" )] - TooManyMipLevels { requested: u32, total: u8 }, + TooManyMipLevels { requested: u32, total: u32 }, #[error("TextureView array layer count + base array layer {requested} must be <= Texture depth/array layer count {total}")] - TooManyArrayLayers { requested: u32, total: u16 }, + TooManyArrayLayers { requested: u32, total: u32 }, #[error("Requested array layer count {requested} is not valid for the target view dimension {dim:?}")] InvalidArrayLayerCount { requested: u32, dim: wgt::TextureViewDimension, }, - #[error("Aspect {requested:?} is not in the source texture ({total:?})")] + #[error("Aspect {requested_aspect:?} is not in the source texture format {texture_format:?}")] InvalidAspect { - requested: hal::format::Aspects, - total: hal::format::Aspects, + texture_format: wgt::TextureFormat, + requested_aspect: wgt::TextureAspect, + }, + #[error("Unable to view texture {texture:?} as {view:?}")] + FormatReinterpretation { + texture: wgt::TextureFormat, + view: wgt::TextureFormat, }, } @@ -364,7 +319,7 @@ pub enum TextureViewDestroyError { SwapChainImage, } -impl Resource for TextureView { +impl Resource for TextureView { const TYPE: &'static str = "TextureView"; fn life_guard(&self) -> &LifeGuard { @@ -372,7 +327,7 @@ impl Resource for TextureView { } } -impl Borrow<()> for TextureView { +impl Borrow<()> for TextureView { fn borrow(&self) -> &() { &DUMMY_SELECTOR } @@ -423,8 +378,8 @@ impl Default for SamplerDescriptor<'_> { } #[derive(Debug)] -pub struct Sampler { - pub(crate) raw: B::Sampler, +pub struct Sampler { + pub(crate) raw: A::Sampler, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, /// `true` if this is a comparison sampler @@ -446,7 +401,7 @@ pub enum CreateSamplerError { MissingFeatures(#[from] MissingFeatures), } -impl Resource for Sampler { +impl Resource for Sampler { const TYPE: &'static str = "Sampler"; fn life_guard(&self) -> &LifeGuard { @@ -454,7 +409,7 @@ impl Resource for Sampler { } } -impl Borrow<()> for Sampler { +impl Borrow<()> for Sampler { fn borrow(&self) -> &() { &DUMMY_SELECTOR } @@ -471,18 +426,17 @@ pub enum CreateQuerySetError { MissingFeatures(#[from] MissingFeatures), } +pub type QuerySetDescriptor<'a> = wgt::QuerySetDescriptor>; + #[derive(Debug)] -pub struct QuerySet { - pub(crate) raw: B::QueryPool, +pub struct QuerySet { + pub(crate) raw: A::QuerySet, pub(crate) device_id: Stored, pub(crate) life_guard: LifeGuard, - /// Amount of queries in the query set. - pub(crate) desc: wgt::QuerySetDescriptor, - /// Amount of numbers in each query (i.e. a pipeline statistics query for two attributes will have this number be two) - pub(crate) elements: u32, + pub(crate) desc: wgt::QuerySetDescriptor<()>, } -impl Resource for QuerySet { +impl Resource for QuerySet { const TYPE: &'static str = "QuerySet"; fn life_guard(&self) -> &LifeGuard { @@ -490,7 +444,7 @@ impl Resource for QuerySet { } } -impl Borrow<()> for QuerySet { +impl Borrow<()> for QuerySet { fn borrow(&self) -> &() { &DUMMY_SELECTOR } diff --git a/wgpu-core/src/swap_chain.rs b/wgpu-core/src/swap_chain.rs index 8156efd5a6..e70b930d67 100644 --- a/wgpu-core/src/swap_chain.rs +++ b/wgpu-core/src/swap_chain.rs @@ -35,35 +35,34 @@ #[cfg(feature = "trace")] use crate::device::trace::Action; use crate::{ - conv, device::DeviceError, - hub::{GfxBackend, Global, GlobalIdentityHandlerFactory, Input, Token}, + hub::{Global, GlobalIdentityHandlerFactory, HalApi, Input, Token}, id::{DeviceId, SwapChainId, TextureViewId, Valid}, resource, track::TextureSelector, - LifeGuard, PrivateFeatures, Stored, SubmissionIndex, + LifeGuard, Stored, SubmissionIndex, }; -use hal::{queue::Queue as _, window::PresentationSurface as _}; +use hal::{Device as _, Queue as _, Surface as _}; +use std::{borrow::Borrow, marker::PhantomData}; use thiserror::Error; -use wgt::{SwapChainDescriptor, SwapChainStatus}; +use wgt::SwapChainStatus as Status; -const FRAME_TIMEOUT_MS: u64 = 1000; +const FRAME_TIMEOUT_MS: u32 = 1000; pub const DESIRED_NUM_FRAMES: u32 = 3; #[derive(Debug)] -pub struct SwapChain { +pub struct SwapChain { pub(crate) life_guard: LifeGuard, pub(crate) device_id: Stored, - pub(crate) desc: SwapChainDescriptor, - pub(crate) num_frames: hal::window::SwapImageIndex, - pub(crate) semaphore: B::Semaphore, - pub(crate) acquired_view_id: Option>, + pub(crate) desc: wgt::SwapChainDescriptor, + pub(crate) num_frames: u32, + pub(crate) acquired_texture: Option<(Stored, A::SurfaceTexture)>, pub(crate) active_submission_index: SubmissionIndex, - pub(crate) framebuffer_attachment: hal::image::FramebufferAttachment, + pub(crate) marker: PhantomData, } -impl crate::hub::Resource for SwapChain { +impl crate::hub::Resource for SwapChain { const TYPE: &'static str = "SwapChain"; fn life_guard(&self) -> &LifeGuard { @@ -99,49 +98,29 @@ pub enum CreateSwapChainError { UnsupportedQueueFamily, #[error("requested format {requested:?} is not in list of supported formats: {available:?}")] UnsupportedFormat { - requested: hal::format::Format, - available: Vec, + requested: wgt::TextureFormat, + available: Vec, }, -} - -pub(crate) fn swap_chain_descriptor_to_hal( - desc: &SwapChainDescriptor, - num_frames: u32, - private_features: PrivateFeatures, -) -> hal::window::SwapchainConfig { - let mut config = hal::window::SwapchainConfig::new( - desc.width, - desc.height, - conv::map_texture_format(desc.format, private_features), - num_frames, - ); - //TODO: check for supported - config.image_usage = conv::map_texture_usage(desc.usage, hal::format::Aspects::COLOR); - config.composite_alpha_mode = hal::window::CompositeAlphaMode::OPAQUE; - config.present_mode = match desc.present_mode { - wgt::PresentMode::Immediate => hal::window::PresentMode::IMMEDIATE, - wgt::PresentMode::Mailbox => hal::window::PresentMode::MAILBOX, - wgt::PresentMode::Fifo => hal::window::PresentMode::FIFO, - }; - config + #[error("requested usage is not supported")] + UnsupportedUsage, } #[repr(C)] #[derive(Debug)] pub struct SwapChainOutput { - pub status: SwapChainStatus, + pub status: Status, pub view_id: Option, } impl Global { - pub fn swap_chain_get_current_texture_view( + pub fn swap_chain_get_current_texture_view( &self, swap_chain_id: SwapChainId, view_id_in: Input, ) -> Result { profiling::scope!("get_next_texture", "SwapChain"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let fid = hub.texture_views.prepare(view_id_in); @@ -155,7 +134,6 @@ impl Global { .get_mut(swap_chain_id) .map_err(|_| SwapChainError::Invalid)?; - #[allow(unused_variables)] let device = &device_guard[sc.device_id.value]; #[cfg(feature = "trace")] if let Some(ref trace) = device.trace { @@ -165,52 +143,72 @@ impl Global { }); } - let suf = B::get_surface_mut(surface); - let (image, status) = match unsafe { suf.acquire_image(FRAME_TIMEOUT_MS * 1_000_000) } { - Ok((surface_image, None)) => (Some(surface_image), SwapChainStatus::Good), - Ok((surface_image, Some(_))) => (Some(surface_image), SwapChainStatus::Suboptimal), + let suf = A::get_surface_mut(surface); + let (texture, status) = match unsafe { suf.acquire_texture(FRAME_TIMEOUT_MS) } { + Ok(Some(ast)) => { + let status = if ast.suboptimal { + Status::Suboptimal + } else { + Status::Good + }; + (Some(ast.texture), status) + } + Ok(None) => (None, Status::Timeout), Err(err) => ( None, match err { - hal::window::AcquireError::OutOfMemory(_) => { - return Err(DeviceError::OutOfMemory.into()) + hal::SurfaceError::Lost => Status::Lost, + hal::SurfaceError::Device(err) => { + return Err(DeviceError::from(err).into()); } - hal::window::AcquireError::NotReady { .. } => SwapChainStatus::Timeout, - hal::window::AcquireError::OutOfDate(_) => SwapChainStatus::Outdated, - hal::window::AcquireError::SurfaceLost(_) => SwapChainStatus::Lost, - hal::window::AcquireError::DeviceLost(_) => { - return Err(DeviceError::Lost.into()) + hal::SurfaceError::Outdated => Status::Outdated, + hal::SurfaceError::Other(msg) => { + log::error!("acquire error: {}", msg); + Status::Lost } }, ), }; - let view_id = match image { - Some(image) => { + let hal_desc = hal::TextureViewDescriptor { + label: Some("_Frame"), + format: sc.desc.format, + dimension: wgt::TextureViewDimension::D2, + usage: hal::TextureUse::COLOR_TARGET, + range: wgt::ImageSubresourceRange::default(), + }; + + let view_id = match texture { + Some(suf_texture) => { + let raw = unsafe { + device + .raw + .create_texture_view(suf_texture.borrow(), &hal_desc) + .map_err(DeviceError::from)? + }; let view = resource::TextureView { - inner: resource::TextureViewInner::SwapChain { - image, - source_id: Stored { - value: Valid(swap_chain_id), - ref_count: sc.life_guard.add_ref(), - }, + raw, + source: resource::TextureViewSource::SwapChain(Stored { + value: Valid(swap_chain_id), + ref_count: sc.life_guard.add_ref(), + }), + desc: resource::HalTextureViewDescriptor { + format: sc.desc.format, + dimension: wgt::TextureViewDimension::D2, + range: wgt::ImageSubresourceRange::default(), }, - aspects: hal::format::Aspects::COLOR, - format: sc.desc.format, format_features: wgt::TextureFormatFeatures { allowed_usages: wgt::TextureUsage::RENDER_ATTACHMENT, flags: wgt::TextureFormatFeatureFlags::empty(), filterable: false, }, - dimension: wgt::TextureViewDimension::D2, extent: wgt::Extent3d { width: sc.desc.width, height: sc.desc.height, depth_or_array_layers: 1, }, samples: 1, - framebuffer_attachment: sc.framebuffer_attachment.clone(), - sampled_internal_use: resource::TextureUse::empty(), + sampled_internal_use: hal::TextureUse::empty(), selector: TextureSelector { layers: 0..1, levels: 0..1, @@ -221,14 +219,17 @@ impl Global { let ref_count = view.life_guard.add_ref(); let id = fid.assign(view, &mut token); - if sc.acquired_view_id.is_some() { + if sc.acquired_texture.is_some() { return Err(SwapChainError::AlreadyAcquired); } - sc.acquired_view_id = Some(Stored { - value: id, - ref_count, - }); + sc.acquired_texture = Some(( + Stored { + value: id, + ref_count, + }, + suf_texture, + )); Some(id.0) } @@ -238,13 +239,13 @@ impl Global { Ok(SwapChainOutput { status, view_id }) } - pub fn swap_chain_present( + pub fn swap_chain_present( &self, swap_chain_id: SwapChainId, - ) -> Result { + ) -> Result { profiling::scope!("present", "SwapChain"); - let hub = B::hub(self); + let hub = A::hub(self); let mut token = Token::root(); let (mut surface_guard, mut token) = self.surfaces.write(&mut token); @@ -252,7 +253,7 @@ impl Global { .get_mut(swap_chain_id.to_surface_id()) .map_err(|_| SwapChainError::InvalidSurface)?; let (mut device_guard, mut token) = hub.devices.write(&mut token); - let (mut swap_chain_guard, mut token) = hub.swap_chains.write(&mut token); + let (mut swap_chain_guard, _) = hub.swap_chains.write(&mut token); let sc = swap_chain_guard .get_mut(swap_chain_id) .map_err(|_| SwapChainError::Invalid)?; @@ -263,43 +264,46 @@ impl Global { trace.lock().add(Action::PresentSwapChain(swap_chain_id)); } - let view = { - let view_id = sc - .acquired_view_id + let suf_texture = { + let (view_id, suf_texture) = sc + .acquired_texture .take() .ok_or(SwapChainError::AlreadyAcquired)?; + + drop(swap_chain_guard); + device.suspect_texture_view_for_destruction(view_id.value, &mut token); + + let (mut view_guard, _) = hub.texture_views.write(&mut token); + let view = &mut view_guard[view_id.value]; + let _ = view.life_guard.ref_count.take(); + + /* let (view_maybe, _) = hub.texture_views.unregister(view_id.value.0, &mut token); - view_maybe.ok_or(SwapChainError::Invalid)? - }; - if view.life_guard.ref_count.unwrap().load() != 1 { - return Err(SwapChainError::StillReferenced); - } - let image = match view.inner { - resource::TextureViewInner::Native { .. } => unreachable!(), - resource::TextureViewInner::SwapChain { image, .. } => image, + drop(view_id); // contains the ref count + let view = view_maybe.ok_or(SwapChainError::Invalid)?; + if view.life_guard.ref_count.unwrap().load() != 1 { + return Err(SwapChainError::StillReferenced); + }*/ + suf_texture }; - let sem = if sc.active_submission_index > device.last_completed_submission_index() { - Some(&mut sc.semaphore) - } else { - None + let result = unsafe { + device + .queue + .present(A::get_surface_mut(surface), suf_texture) }; - let queue = &mut device.queue_group.queues[0]; - let result = unsafe { queue.present(B::get_surface_mut(surface), image, sem) }; log::debug!("Presented. End of Frame"); match result { - Ok(None) => Ok(SwapChainStatus::Good), - Ok(Some(_)) => Ok(SwapChainStatus::Suboptimal), + Ok(()) => Ok(Status::Good), Err(err) => match err { - hal::window::PresentError::OutOfMemory(_) => { - Err(SwapChainError::Device(DeviceError::OutOfMemory)) - } - hal::window::PresentError::OutOfDate(_) => Ok(SwapChainStatus::Outdated), - hal::window::PresentError::SurfaceLost(_) => Ok(SwapChainStatus::Lost), - hal::window::PresentError::DeviceLost(_) => { - Err(SwapChainError::Device(DeviceError::Lost)) + hal::SurfaceError::Lost => Ok(Status::Lost), + hal::SurfaceError::Device(err) => Err(SwapChainError::from(DeviceError::from(err))), + hal::SurfaceError::Outdated => Ok(Status::Outdated), + hal::SurfaceError::Other(msg) => { + log::error!("acquire error: {}", msg); + Err(SwapChainError::InvalidSurface) } }, } diff --git a/wgpu-core/src/track/buffer.rs b/wgpu-core/src/track/buffer.rs index e4999a9ae4..c0cdee2060 100644 --- a/wgpu-core/src/track/buffer.rs +++ b/wgpu-core/src/track/buffer.rs @@ -3,12 +3,9 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::{PendingTransition, ResourceState, Unit}; -use crate::{ - id::{BufferId, Valid}, - resource::BufferUse, -}; +use crate::id::{BufferId, Valid}; +use hal::BufferUse; -//TODO: store `hal::buffer::State` here to avoid extra conversions pub(crate) type BufferState = Unit; impl PendingTransition { diff --git a/wgpu-core/src/track/mod.rs b/wgpu-core/src/track/mod.rs index e61765969b..a7038a83cf 100644 --- a/wgpu-core/src/track/mod.rs +++ b/wgpu-core/src/track/mod.rs @@ -7,12 +7,14 @@ mod range; mod texture; use crate::{ - conv, hub, + hub, id::{self, TypedId, Valid}, resource, Epoch, FastHashMap, Index, RefCount, }; -use std::{collections::hash_map::Entry, fmt, marker::PhantomData, ops, vec::Drain}; +use std::{ + collections::hash_map::Entry, fmt, marker::PhantomData, num::NonZeroU32, ops, vec::Drain, +}; use thiserror::Error; pub(crate) use buffer::BufferState; @@ -125,19 +127,16 @@ pub(crate) struct PendingTransition { } impl PendingTransition { - /// Produce the gfx-hal barrier corresponding to the transition. - pub fn into_hal<'a, B: hal::Backend>( + /// Produce the hal barrier corresponding to the transition. + pub fn into_hal<'a, A: hal::Api>( self, - buf: &'a resource::Buffer, - ) -> hal::memory::Barrier<'a, B> { + buf: &'a resource::Buffer, + ) -> hal::BufferBarrier<'a, A> { log::trace!("\tbuffer -> {:?}", self); - let &(ref target, _) = buf.raw.as_ref().expect("Buffer is destroyed"); - hal::memory::Barrier::Buffer { - states: conv::map_buffer_state(self.usage.start) - ..conv::map_buffer_state(self.usage.end), - target, - range: hal::buffer::SubRange::WHOLE, - families: None, + let buffer = buf.raw.as_ref().expect("Buffer is destroyed"); + hal::BufferBarrier { + buffer, + usage: self.usage, } } } @@ -152,26 +151,27 @@ impl From> for UsageConflict { } impl PendingTransition { - /// Produce the gfx-hal barrier corresponding to the transition. - pub fn into_hal<'a, B: hal::Backend>( + /// Produce the hal barrier corresponding to the transition. + pub fn into_hal<'a, A: hal::Api>( self, - tex: &'a resource::Texture, - ) -> hal::memory::Barrier<'a, B> { + tex: &'a resource::Texture, + ) -> hal::TextureBarrier<'a, A> { log::trace!("\ttexture -> {:?}", self); - let &(ref target, _) = tex.raw.as_ref().expect("Texture is destroyed"); - let aspects = tex.aspects; - hal::memory::Barrier::Image { - states: conv::map_texture_state(self.usage.start, aspects) - ..conv::map_texture_state(self.usage.end, aspects), - target, - range: hal::image::SubresourceRange { - aspects, - level_start: self.selector.levels.start, - level_count: Some(self.selector.levels.end - self.selector.levels.start), - layer_start: self.selector.layers.start, - layer_count: Some(self.selector.layers.end - self.selector.layers.start), + let texture = tex.raw.as_ref().expect("Texture is destroyed"); + hal::TextureBarrier { + texture, + range: wgt::ImageSubresourceRange { + aspect: wgt::TextureAspect::All, + base_mip_level: self.selector.levels.start, + mip_level_count: NonZeroU32::new( + self.selector.levels.end - self.selector.levels.start, + ), + base_array_layer: self.selector.layers.start, + array_layer_count: NonZeroU32::new( + self.selector.layers.end - self.selector.layers.start, + ), }, - families: None, + usage: self.usage, } } } @@ -180,8 +180,8 @@ impl From> for UsageConflict { fn from(e: PendingTransition) -> Self { Self::Texture { id: e.id.0, - mip_levels: e.selector.levels.start as u32..e.selector.levels.end as u32, - array_layers: e.selector.layers.start as u32..e.selector.layers.end as u32, + mip_levels: e.selector.levels.start..e.selector.levels.end, + array_layers: e.selector.layers.start..e.selector.layers.end, combined_use: e.usage.end, } } @@ -230,7 +230,7 @@ impl ResourceTracker { } /// Remove an id from the tracked map. - pub(crate) fn remove(&mut self, id: Valid) -> bool { + pub(crate) fn _remove(&mut self, id: Valid) -> bool { let (index, epoch, backend) = id.0.unzip(); debug_assert_eq!(backend, self.backend); match self.map.remove(&index) { @@ -557,14 +557,14 @@ pub enum UsageConflict { )] Buffer { id: id::BufferId, - combined_use: resource::BufferUse, + combined_use: hal::BufferUse, }, #[error("Attempted to use texture {id:?} mips {mip_levels:?} layers {array_layers:?} as a combination of {combined_use:?} within a usage scope.")] Texture { id: id::TextureId, mip_levels: ops::Range, array_layers: ops::Range, - combined_use: resource::TextureUse, + combined_use: hal::TextureUse, }, } @@ -599,7 +599,7 @@ impl TrackerSet { } /// Clear all the trackers. - pub fn clear(&mut self) { + pub fn _clear(&mut self) { self.buffers.clear(); self.textures.clear(); self.views.clear(); diff --git a/wgpu-core/src/track/texture.rs b/wgpu-core/src/track/texture.rs index 6d1d4a5935..2f01c72a1a 100644 --- a/wgpu-core/src/track/texture.rs +++ b/wgpu-core/src/track/texture.rs @@ -3,29 +3,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use super::{range::RangedStates, PendingTransition, ResourceState, Unit}; -use crate::{ - device::MAX_MIP_LEVELS, - id::{TextureId, Valid}, - resource::TextureUse, -}; +use crate::id::{TextureId, Valid}; +use hal::TextureUse; use arrayvec::ArrayVec; use std::{iter, ops::Range}; -//TODO: store `hal::image::State` here to avoid extra conversions -type PlaneStates = RangedStates>; +type PlaneStates = RangedStates>; #[derive(Clone, Debug, PartialEq, Eq)] pub struct TextureSelector { - //pub aspects: hal::format::Aspects, - pub levels: Range, - pub layers: Range, + //TODO: rename to `mip_levels` and `array_layers` for consistency + //pub aspects: hal::FormatAspect, + pub levels: Range, + pub layers: Range, } #[derive(Clone, Debug, Default, PartialEq)] pub(crate) struct TextureState { - mips: ArrayVec<[PlaneStates; MAX_MIP_LEVELS as usize]>, + mips: ArrayVec<[PlaneStates; hal::MAX_MIP_LEVELS as usize]>, /// True if we have the information about all the subresources here full: bool, } @@ -44,7 +41,7 @@ impl PendingTransition { } impl TextureState { - pub fn new(mip_level_count: hal::image::Level, array_layer_count: hal::image::Layer) -> Self { + pub fn new(mip_level_count: u32, array_layer_count: u32) -> Self { Self { mips: iter::repeat_with(|| { PlaneStates::from_range(0..array_layer_count, Unit::new(TextureUse::UNINITIALIZED)) @@ -104,7 +101,7 @@ impl ResourceState for TextureState { .iter_mut() .enumerate() { - let level = selector.levels.start + mip_id as hal::image::Level; + let level = selector.levels.start + mip_id as u32; let layers = mip.isolate(&selector.layers, Unit::new(usage)); for &mut (ref range, ref mut unit) in layers { if unit.last == usage && TextureUse::ORDERED.contains(usage) { @@ -154,7 +151,7 @@ impl ResourceState for TextureState { .iter_mut() .enumerate() { - let level = selector.levels.start + mip_id as hal::image::Level; + let level = selector.levels.start + mip_id as u32; let layers = mip.isolate(&selector.layers, Unit::new(usage)); for &mut (ref range, ref mut unit) in layers { match unit.first { @@ -193,7 +190,7 @@ impl ResourceState for TextureState { } for (mip_id, (mip_self, mip_other)) in self.mips.iter_mut().zip(&other.mips).enumerate() { - let level = mip_id as hal::image::Level; + let level = mip_id as u32; temp.extend(mip_self.merge(mip_other, 0)); mip_self.clear(); @@ -375,7 +372,7 @@ mod test { 2..3, Unit { first: Some(TextureUse::COPY_SRC), - last: TextureUse::ATTACHMENT_WRITE, + last: TextureUse::COLOR_TARGET, }, ), ]); @@ -416,7 +413,7 @@ mod test { ts1.mips[0].query(&(2..3), |&v| v), Some(Ok(Unit { first: Some(TextureUse::SAMPLED), - last: TextureUse::ATTACHMENT_WRITE, + last: TextureUse::COLOR_TARGET, })), "wrong final layer 2 state" ); @@ -425,7 +422,7 @@ mod test { ts2.mips[0] = PlaneStates::from_slice(&[( 2..3, Unit { - first: Some(TextureUse::ATTACHMENT_WRITE), + first: Some(TextureUse::COLOR_TARGET), last: TextureUse::COPY_SRC, }, )]); diff --git a/wgpu-core/src/validation.rs b/wgpu-core/src/validation.rs index 56812c67c8..0477a03d38 100644 --- a/wgpu-core/src/validation.rs +++ b/wgpu-core/src/validation.rs @@ -631,8 +631,6 @@ impl NumericType { | Tf::Bc7RgbaUnormSrgb | Tf::Etc2RgbA1Unorm | Tf::Etc2RgbA1UnormSrgb - | Tf::Etc2RgbA8Unorm - | Tf::Etc2RgbA8UnormSrgb | Tf::Astc4x4RgbaUnorm | Tf::Astc4x4RgbaUnormSrgb | Tf::Astc5x4RgbaUnorm diff --git a/wgpu-hal/Cargo.toml b/wgpu-hal/Cargo.toml new file mode 100644 index 0000000000..f7183189e1 --- /dev/null +++ b/wgpu-hal/Cargo.toml @@ -0,0 +1,60 @@ +[package] +name = "wgpu-hal" +version = "0.1.0" +authors = ["wgpu developers"] +edition = "2018" +description = "WebGPU hardware abstraction layer" +homepage = "https://github.com/gfx-rs/wgpu" +repository = "https://github.com/gfx-rs/wgpu" +keywords = ["graphics"] +license = "MIT OR Apache-2.0" + +[lib] + +[features] +default = [] +metal = ["naga/msl-out", "block", "foreign-types"] +vulkan = ["naga/spv-out", "ash", "gpu-alloc", "gpu-descriptor", "libloading", "inplace_it", "renderdoc-sys"] + +[dependencies] +bitflags = "1.0" +parking_lot = "0.11" +raw-window-handle = "0.3" +thiserror = "1" +wgt = { package = "wgpu-types", path = "../wgpu-types" } + +# backends common +arrayvec = "0.5" +fxhash = "0.2.1" +libloading = { version = "0.7", optional = true } +log = "0.4" +# backend: Metal +block = { version = "0.1", optional = true } +foreign-types = { version = "0.3", optional = true } +# backend: Vulkan +ash = { version = "0.32", optional = true } +gpu-alloc = { version = "0.4", optional = true } +gpu-descriptor = { version = "0.1", optional = true } +inplace_it = { version ="0.3.3", optional = true } +renderdoc-sys = { version = "0.7.1", optional = true } + +[target.'cfg(windows)'.dependencies] +winapi = { version = "0.3", features = ["libloaderapi", "windef", "winuser"] } + +[target.'cfg(any(target_os="macos", target_os="ios"))'.dependencies] +mtl = { package = "metal", version = "0.22", git="https://github.com/gfx-rs/metal-rs", rev="08cc15a3be5a57fc07bb27091eff3569dd60cfd3" } +objc = "0.2.5" +core-graphics-types = "0.1" + +[dependencies.naga] +git = "https://github.com/gfx-rs/naga" +tag = "gfx-25" + +[dev-dependencies.naga] +git = "https://github.com/gfx-rs/naga" +tag = "gfx-25" +features = ["wgsl-in"] + +[dev-dependencies] +env_logger = "0.8" +winit = "0.24" diff --git a/wgpu-hal/README.md b/wgpu-hal/README.md new file mode 100644 index 0000000000..0ab7a0283a --- /dev/null +++ b/wgpu-hal/README.md @@ -0,0 +1,23 @@ +*wgpu-hal* is an explicit low-level GPU abstraction powering *wgpu-core*. +It's a spiritual successor to [gfx-hal](https://github.com/gfx-rs/gfx), +but with reduced scope, and oriented towards WebGPU implementation goals. + +It has no overhead for validation or tracking, and the API translation overhead is kept to the bare minimum by the design of WebGPU. +This API can be used for resource-demanding applications and engines. + +# Usage notes + +All of the API is `unsafe`. Documenting the exact safety requirements for the +state and function arguments is desired, but will likely be incomplete while the library is in early development. + +The returned errors are only for cases that the user can't anticipate, +such as running out-of-memory, or losing the device. +For the counter-example, there is no error for mapping a buffer that's not mappable. +As the buffer creator, the user should already know if they can map it. + +The API accept iterators in order to avoid forcing the user to store data in particular containers. The implementation doesn't guarantee that any of the iterators are drained, unless stated otherwise by the function documentation. +For this reason, we recommend that iterators don't do any mutating work. + +# Debugging + +Most of the information in https://github.com/gfx-rs/wgpu/wiki/Debugging-wgpu-Applications still applies to this API, with an exception of API tracing/replay functionality, which is only available in *wgpu-core*. diff --git a/wgpu-hal/examples/halmark/main.rs b/wgpu-hal/examples/halmark/main.rs new file mode 100644 index 0000000000..e75758e6c5 --- /dev/null +++ b/wgpu-hal/examples/halmark/main.rs @@ -0,0 +1,711 @@ +extern crate wgpu_hal as hal; + +use hal::{ + Adapter as _, CommandEncoder as _, Device as _, Instance as _, Queue as _, Surface as _, +}; + +use std::{borrow::Borrow, iter, mem, num::NonZeroU32, ptr, time::Instant}; + +const MAX_BUNNIES: usize = 1 << 20; +const BUNNY_SIZE: f32 = 0.15 * 256.0; +const GRAVITY: f32 = -9.8 * 100.0; +const MAX_VELOCITY: f32 = 750.0; + +#[repr(C)] +#[derive(Clone, Copy)] +struct Globals { + mvp: [[f32; 4]; 4], + size: [f32; 2], + pad: [f32; 2], +} + +#[repr(C, align(256))] +#[derive(Clone, Copy)] +struct Locals { + position: [f32; 2], + velocity: [f32; 2], + color: u32, + _pad: u32, +} + +struct UsedResources { + view: A::TextureView, + cmd_buf: A::CommandBuffer, +} + +#[allow(dead_code)] +struct Example { + instance: A::Instance, + adapter: A::Adapter, + surface: A::Surface, + surface_format: wgt::TextureFormat, + device: A::Device, + queue: A::Queue, + global_group: A::BindGroup, + local_group: A::BindGroup, + global_group_layout: A::BindGroupLayout, + local_group_layout: A::BindGroupLayout, + pipeline_layout: A::PipelineLayout, + shader: A::ShaderModule, + pipeline: A::RenderPipeline, + bunnies: Vec, + local_buffer: A::Buffer, + global_buffer: A::Buffer, + sampler: A::Sampler, + texture: A::Texture, + view: A::TextureView, + fence: A::Fence, + fence_value: hal::FenceValue, + cmd_encoder: A::CommandEncoder, + old_resources: Vec<(hal::FenceValue, UsedResources)>, + extent: [u32; 2], + start: Instant, +} + +impl Example { + fn init(window: &winit::window::Window) -> Result { + let instance_desc = hal::InstanceDescriptor { + name: "example", + flags: hal::InstanceFlag::all(), + }; + let instance = unsafe { A::Instance::init(&instance_desc)? }; + let mut surface = unsafe { instance.create_surface(window).unwrap() }; + + let adapter = unsafe { + let mut adapters = instance.enumerate_adapters(); + if adapters.is_empty() { + return Err(hal::InstanceError); + } + let exposed = adapters.swap_remove(0); + println!( + "Surface caps: {:?}", + exposed.adapter.surface_capabilities(&surface) + ); + exposed.adapter + }; + let hal::OpenDevice { device, mut queue } = + unsafe { adapter.open(wgt::Features::empty()).unwrap() }; + + let window_size: (u32, u32) = window.inner_size().into(); + let surface_config = hal::SurfaceConfiguration { + swap_chain_size: 2, + present_mode: wgt::PresentMode::Fifo, + composite_alpha_mode: hal::CompositeAlphaMode::Opaque, + format: wgt::TextureFormat::Bgra8UnormSrgb, + extent: wgt::Extent3d { + width: window_size.0, + height: window_size.1, + depth_or_array_layers: 1, + }, + usage: hal::TextureUse::COLOR_TARGET, + }; + unsafe { + surface.configure(&device, &surface_config).unwrap(); + }; + + let naga_shader = { + let shader_file = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")) + .join("examples") + .join("halmark") + .join("shader.wgsl"); + let source = std::fs::read_to_string(shader_file).unwrap(); + let module = naga::front::wgsl::Parser::new().parse(&source).unwrap(); + let info = naga::valid::Validator::new( + naga::valid::ValidationFlags::all(), + naga::valid::Capabilities::empty(), + ) + .validate(&module) + .unwrap(); + hal::NagaShader { module, info } + }; + let shader_desc = hal::ShaderModuleDescriptor { label: None }; + let shader = unsafe { + device + .create_shader_module(&shader_desc, naga_shader) + .unwrap() + }; + + let global_bgl_desc = hal::BindGroupLayoutDescriptor { + label: None, + entries: &[ + wgt::BindGroupLayoutEntry { + binding: 0, + visibility: wgt::ShaderStage::VERTEX, + ty: wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: wgt::BufferSize::new(mem::size_of::() as _), + }, + count: None, + }, + wgt::BindGroupLayoutEntry { + binding: 1, + visibility: wgt::ShaderStage::FRAGMENT, + ty: wgt::BindingType::Texture { + sample_type: wgt::TextureSampleType::Float { filterable: true }, + view_dimension: wgt::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgt::BindGroupLayoutEntry { + binding: 2, + visibility: wgt::ShaderStage::FRAGMENT, + ty: wgt::BindingType::Sampler { + filtering: true, + comparison: false, + }, + count: None, + }, + ], + }; + + let global_group_layout = + unsafe { device.create_bind_group_layout(&global_bgl_desc).unwrap() }; + + let local_bgl_desc = hal::BindGroupLayoutDescriptor { + entries: &[wgt::BindGroupLayoutEntry { + binding: 0, + visibility: wgt::ShaderStage::VERTEX, + ty: wgt::BindingType::Buffer { + ty: wgt::BufferBindingType::Uniform, + has_dynamic_offset: true, + min_binding_size: wgt::BufferSize::new(mem::size_of::() as _), + }, + count: None, + }], + label: None, + }; + let local_group_layout = + unsafe { device.create_bind_group_layout(&local_bgl_desc).unwrap() }; + + let pipeline_layout_desc = hal::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[&global_group_layout, &local_group_layout], + push_constant_ranges: &[], + }; + let pipeline_layout = unsafe { + device + .create_pipeline_layout(&pipeline_layout_desc) + .unwrap() + }; + + let pipeline_desc = hal::RenderPipelineDescriptor { + label: None, + layout: &pipeline_layout, + vertex_stage: hal::ProgrammableStage { + module: &shader, + entry_point: "vs_main", + }, + vertex_buffers: &[], + fragment_stage: Some(hal::ProgrammableStage { + module: &shader, + entry_point: "fs_main", + }), + primitive: wgt::PrimitiveState { + topology: wgt::PrimitiveTopology::TriangleStrip, + ..wgt::PrimitiveState::default() + }, + depth_stencil: None, + multisample: wgt::MultisampleState::default(), + color_targets: &[wgt::ColorTargetState { + format: surface_config.format, + blend: Some(wgt::BlendState::ALPHA_BLENDING), + write_mask: wgt::ColorWrite::default(), + }], + }; + let pipeline = unsafe { device.create_render_pipeline(&pipeline_desc).unwrap() }; + + let texture_data = vec![0xFFu8; 4]; + + let staging_buffer_desc = hal::BufferDescriptor { + label: Some("stage"), + size: texture_data.len() as wgt::BufferAddress, + usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::COPY_SRC, + memory_flags: hal::MemoryFlag::TRANSIENT, + }; + let staging_buffer = unsafe { device.create_buffer(&staging_buffer_desc).unwrap() }; + unsafe { + let mapping = device + .map_buffer(&staging_buffer, 0..staging_buffer_desc.size) + .unwrap(); + ptr::copy_nonoverlapping( + texture_data.as_ptr(), + mapping.ptr.as_ptr(), + texture_data.len(), + ); + device.unmap_buffer(&staging_buffer).unwrap(); + assert!(mapping.is_coherent); + } + + let texture_desc = hal::TextureDescriptor { + label: None, + size: wgt::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgt::TextureDimension::D2, + format: wgt::TextureFormat::Rgba8UnormSrgb, + usage: hal::TextureUse::COPY_DST | hal::TextureUse::SAMPLED, + memory_flags: hal::MemoryFlag::empty(), + }; + let texture = unsafe { device.create_texture(&texture_desc).unwrap() }; + + let cmd_encoder_desc = hal::CommandEncoderDescriptor { + label: None, + queue: &queue, + }; + let mut cmd_encoder = unsafe { device.create_command_encoder(&cmd_encoder_desc).unwrap() }; + unsafe { cmd_encoder.begin_encoding(Some("init")).unwrap() }; + { + let buffer_barrier = hal::BufferBarrier { + buffer: &staging_buffer, + usage: hal::BufferUse::empty()..hal::BufferUse::COPY_SRC, + }; + let texture_barrier1 = hal::TextureBarrier { + texture: &texture, + range: wgt::ImageSubresourceRange::default(), + usage: hal::TextureUse::UNINITIALIZED..hal::TextureUse::COPY_DST, + }; + let texture_barrier2 = hal::TextureBarrier { + texture: &texture, + range: wgt::ImageSubresourceRange::default(), + usage: hal::TextureUse::COPY_DST..hal::TextureUse::SAMPLED, + }; + let copy = hal::BufferTextureCopy { + buffer_layout: wgt::ImageDataLayout { + offset: 0, + bytes_per_row: NonZeroU32::new(4), + rows_per_image: None, + }, + texture_base: hal::TextureCopyBase { + origin: wgt::Origin3d::ZERO, + mip_level: 0, + aspect: hal::FormatAspect::COLOR, + }, + size: texture_desc.size, + }; + unsafe { + cmd_encoder.transition_buffers(iter::once(buffer_barrier)); + cmd_encoder.transition_textures(iter::once(texture_barrier1)); + cmd_encoder.copy_buffer_to_texture(&staging_buffer, &texture, iter::once(copy)); + cmd_encoder.transition_textures(iter::once(texture_barrier2)); + } + } + + let sampler_desc = hal::SamplerDescriptor { + label: None, + address_modes: [wgt::AddressMode::ClampToEdge; 3], + mag_filter: wgt::FilterMode::Linear, + min_filter: wgt::FilterMode::Nearest, + mipmap_filter: wgt::FilterMode::Nearest, + lod_clamp: None, + compare: None, + anisotropy_clamp: None, + border_color: None, + }; + let sampler = unsafe { device.create_sampler(&sampler_desc).unwrap() }; + + let globals = Globals { + // cgmath::ortho() projection + mvp: [ + [2.0 / window_size.0 as f32, 0.0, 0.0, 0.0], + [0.0, 2.0 / window_size.1 as f32, 0.0, 0.0], + [0.0, 0.0, 1.0, 0.0], + [-1.0, -1.0, 0.0, 1.0], + ], + size: [BUNNY_SIZE; 2], + pad: [0.0; 2], + }; + + let global_buffer_desc = hal::BufferDescriptor { + label: Some("global"), + size: mem::size_of::() as wgt::BufferAddress, + usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::UNIFORM, + memory_flags: hal::MemoryFlag::empty(), + }; + let global_buffer = unsafe { + let buffer = device.create_buffer(&global_buffer_desc).unwrap(); + let mapping = device + .map_buffer(&buffer, 0..global_buffer_desc.size) + .unwrap(); + ptr::copy_nonoverlapping( + &globals as *const Globals as *const u8, + mapping.ptr.as_ptr(), + mem::size_of::(), + ); + device.unmap_buffer(&buffer).unwrap(); + assert!(mapping.is_coherent); + buffer + }; + + let local_buffer_desc = hal::BufferDescriptor { + label: Some("local"), + size: (MAX_BUNNIES as wgt::BufferAddress) * wgt::BIND_BUFFER_ALIGNMENT, + usage: hal::BufferUse::MAP_WRITE | hal::BufferUse::UNIFORM, + memory_flags: hal::MemoryFlag::empty(), + }; + let local_buffer = unsafe { device.create_buffer(&local_buffer_desc).unwrap() }; + + let view_desc = hal::TextureViewDescriptor { + label: None, + format: texture_desc.format, + dimension: wgt::TextureViewDimension::D2, + usage: hal::TextureUse::SAMPLED, + range: wgt::ImageSubresourceRange::default(), + }; + let view = unsafe { device.create_texture_view(&texture, &view_desc).unwrap() }; + + let global_group = { + let global_buffer_binding = hal::BufferBinding { + buffer: &global_buffer, + offset: 0, + size: None, + }; + let texture_binding = hal::TextureBinding { + view: &view, + usage: hal::TextureUse::SAMPLED, + }; + let global_group_desc = hal::BindGroupDescriptor { + label: Some("global"), + layout: &global_group_layout, + buffers: &[global_buffer_binding], + samplers: &[&sampler], + textures: &[texture_binding], + entries: &[ + hal::BindGroupEntry { + binding: 0, + resource_index: 0, + }, + hal::BindGroupEntry { + binding: 1, + resource_index: 0, + }, + hal::BindGroupEntry { + binding: 2, + resource_index: 0, + }, + ], + }; + unsafe { device.create_bind_group(&global_group_desc).unwrap() } + }; + + let local_group = { + let local_buffer_binding = hal::BufferBinding { + buffer: &local_buffer, + offset: 0, + size: wgt::BufferSize::new(mem::size_of::() as _), + }; + let local_group_desc = hal::BindGroupDescriptor { + label: Some("local"), + layout: &local_group_layout, + buffers: &[local_buffer_binding], + samplers: &[], + textures: &[], + entries: &[hal::BindGroupEntry { + binding: 0, + resource_index: 0, + }], + }; + unsafe { device.create_bind_group(&local_group_desc).unwrap() } + }; + + let fence = unsafe { + let mut fence = device.create_fence().unwrap(); + let init_cmd = cmd_encoder.end_encoding().unwrap(); + queue.submit(&[&init_cmd], Some((&mut fence, 1))).unwrap(); + device.wait(&fence, 1, !0).unwrap(); + device.destroy_buffer(staging_buffer); + cmd_encoder.reset_all(iter::once(init_cmd)); + fence + }; + + Ok(Example { + instance, + surface, + surface_format: surface_config.format, + adapter, + device, + queue, + pipeline_layout, + shader, + pipeline, + global_group, + local_group, + global_group_layout, + local_group_layout, + bunnies: Vec::new(), + local_buffer, + global_buffer, + sampler, + texture, + view, + old_resources: Vec::new(), + cmd_encoder, + fence, + fence_value: 1, + extent: [window_size.0, window_size.1], + start: Instant::now(), + }) + } + + fn exit(mut self) { + unsafe { + self.device.wait(&self.fence, self.fence_value, !0).unwrap(); + let mut cmd_buffers = Vec::new(); + for (_, old) in self.old_resources { + self.device.destroy_texture_view(old.view); + cmd_buffers.push(old.cmd_buf); + } + self.cmd_encoder.reset_all(cmd_buffers.into_iter()); + self.surface.unconfigure(&self.device); + + self.device.destroy_bind_group(self.local_group); + self.device.destroy_bind_group(self.global_group); + self.device.destroy_buffer(self.local_buffer); + self.device.destroy_buffer(self.global_buffer); + self.device.destroy_texture_view(self.view); + self.device.destroy_command_encoder(self.cmd_encoder); + self.device.destroy_fence(self.fence); + self.device.destroy_texture(self.texture); + self.device.destroy_sampler(self.sampler); + self.device.destroy_shader_module(self.shader); + self.device.destroy_render_pipeline(self.pipeline); + self.device + .destroy_bind_group_layout(self.local_group_layout); + self.device + .destroy_bind_group_layout(self.global_group_layout); + self.device.destroy_pipeline_layout(self.pipeline_layout); + + self.device.exit(); + self.instance.destroy_surface(self.surface); + } + } + + fn update(&mut self, event: winit::event::WindowEvent) { + if let winit::event::WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: Some(winit::event::VirtualKeyCode::Space), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } = event + { + let spawn_count = 64 + self.bunnies.len() / 2; + let elapsed = self.start.elapsed(); + let color = elapsed.as_nanos() as u32; + println!( + "Spawning {} bunnies, total at {}", + spawn_count, + self.bunnies.len() + spawn_count + ); + for i in 0..spawn_count { + let random = ((elapsed.as_nanos() * (i + 1) as u128) & 0xFF) as f32 / 255.0; + let speed = random * MAX_VELOCITY - (MAX_VELOCITY * 0.5); + self.bunnies.push(Locals { + position: [0.0, 0.5 * (self.extent[1] as f32)], + velocity: [speed, 0.0], + color, + _pad: 0, + }); + } + } + } + + fn render(&mut self) { + let delta = 0.01; + for bunny in self.bunnies.iter_mut() { + bunny.position[0] += bunny.velocity[0] * delta; + bunny.position[1] += bunny.velocity[1] * delta; + bunny.velocity[1] += GRAVITY * delta; + if (bunny.velocity[0] > 0.0 + && bunny.position[0] + 0.5 * BUNNY_SIZE > self.extent[0] as f32) + || (bunny.velocity[0] < 0.0 && bunny.position[0] - 0.5 * BUNNY_SIZE < 0.0) + { + bunny.velocity[0] *= -1.0; + } + if bunny.velocity[1] < 0.0 && bunny.position[1] < 0.5 * BUNNY_SIZE { + bunny.velocity[1] *= -1.0; + } + } + + if !self.bunnies.is_empty() { + let size = self.bunnies.len() * wgt::BIND_BUFFER_ALIGNMENT as usize; + unsafe { + let mapping = self + .device + .map_buffer(&self.local_buffer, 0..size as wgt::BufferAddress) + .unwrap(); + ptr::copy_nonoverlapping( + self.bunnies.as_ptr() as *const u8, + mapping.ptr.as_ptr(), + size, + ); + assert!(mapping.is_coherent); + self.device.unmap_buffer(&self.local_buffer).unwrap(); + } + } + + unsafe { + self.cmd_encoder.begin_encoding(Some("frame")).unwrap(); + } + + let surface_tex = unsafe { self.surface.acquire_texture(!0).unwrap().unwrap().texture }; + let surface_view_desc = hal::TextureViewDescriptor { + label: None, + format: self.surface_format, + dimension: wgt::TextureViewDimension::D2, + usage: hal::TextureUse::COLOR_TARGET, + range: wgt::ImageSubresourceRange::default(), + }; + let surface_tex_view = unsafe { + self.device + .create_texture_view(surface_tex.borrow(), &surface_view_desc) + .unwrap() + }; + let pass_desc = hal::RenderPassDescriptor { + label: None, + color_attachments: &[hal::ColorAttachment { + target: hal::Attachment { + view: &surface_tex_view, + usage: hal::TextureUse::COLOR_TARGET, + boundary_usage: hal::TextureUse::UNINITIALIZED..hal::TextureUse::empty(), + }, + resolve_target: None, + ops: hal::AttachmentOp::STORE, + clear_value: wgt::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }, + }], + depth_stencil_attachment: None, + }; + unsafe { + self.cmd_encoder.begin_render_pass(&pass_desc); + self.cmd_encoder.set_render_pipeline(&self.pipeline); + self.cmd_encoder + .set_bind_group(&self.pipeline_layout, 0, &self.global_group, &[]); + } + + for i in 0..self.bunnies.len() { + let offset = + (i as wgt::DynamicOffset) * (wgt::BIND_BUFFER_ALIGNMENT as wgt::DynamicOffset); + unsafe { + self.cmd_encoder.set_bind_group( + &self.pipeline_layout, + 1, + &self.local_group, + &[offset], + ); + self.cmd_encoder.draw(0, 4, 0, 1); + } + } + + self.fence_value += 1; + let last_done = unsafe { self.device.get_fence_value(&self.fence).unwrap() }; + for i in (0..self.old_resources.len()).rev() { + if self.old_resources[i].0 <= last_done { + let (_, old) = self.old_resources.swap_remove(i); + unsafe { + self.device.destroy_texture_view(old.view); + //TODO: destroy the command buffer + } + } + } + + let cmd_buf = unsafe { + self.cmd_encoder.end_render_pass(); + let cmd_buf = self.cmd_encoder.end_encoding().unwrap(); + self.queue + .submit(&[&cmd_buf], Some((&mut self.fence, self.fence_value))) + .unwrap(); + self.queue.present(&mut self.surface, surface_tex).unwrap(); + cmd_buf + }; + + self.old_resources.push(( + self.fence_value, + UsedResources { + view: surface_tex_view, + cmd_buf, + }, + )); + } +} + +#[cfg(all(feature = "metal"))] +type Api = hal::api::Metal; +#[cfg(all(feature = "vulkan", not(feature = "metal")))] +type Api = hal::api::Vulkan; +#[cfg(all(not(feature = "vulkan"), not(feature = "metal")))] +type Api = hal::api::Empty; + +fn main() { + env_logger::init(); + + let event_loop = winit::event_loop::EventLoop::new(); + let window = winit::window::WindowBuilder::new() + .with_title("hal-bunnymark") + .build(&event_loop) + .unwrap(); + + let example_result = Example::::init(&window); + let mut example = Some(example_result.expect("Selected backend is not supported")); + + let mut last_frame_inst = Instant::now(); + let (mut frame_count, mut accum_time) = (0, 0.0); + + event_loop.run(move |event, _, control_flow| { + let _ = &window; // force ownership by the closure + *control_flow = winit::event_loop::ControlFlow::Poll; + match event { + winit::event::Event::RedrawEventsCleared => { + window.request_redraw(); + } + winit::event::Event::WindowEvent { event, .. } => match event { + winit::event::WindowEvent::KeyboardInput { + input: + winit::event::KeyboardInput { + virtual_keycode: Some(winit::event::VirtualKeyCode::Escape), + state: winit::event::ElementState::Pressed, + .. + }, + .. + } + | winit::event::WindowEvent::CloseRequested => { + *control_flow = winit::event_loop::ControlFlow::Exit; + } + _ => { + example.as_mut().unwrap().update(event); + } + }, + winit::event::Event::RedrawRequested(_) => { + { + accum_time += last_frame_inst.elapsed().as_secs_f32(); + last_frame_inst = Instant::now(); + frame_count += 1; + if frame_count == 100 { + println!( + "Avg frame time {}ms", + accum_time * 1000.0 / frame_count as f32 + ); + accum_time = 0.0; + frame_count = 0; + } + } + example.as_mut().unwrap().render(); + } + winit::event::Event::LoopDestroyed => { + example.take().unwrap().exit(); + } + _ => {} + } + }); +} diff --git a/wgpu/examples/bunnymark/shader.wgsl b/wgpu-hal/examples/halmark/shader.wgsl similarity index 100% rename from wgpu/examples/bunnymark/shader.wgsl rename to wgpu-hal/examples/halmark/shader.wgsl diff --git a/wgpu-hal/src/empty.rs b/wgpu-hal/src/empty.rs new file mode 100644 index 0000000000..960b383057 --- /dev/null +++ b/wgpu-hal/src/empty.rs @@ -0,0 +1,385 @@ +#![allow(unused_variables)] + +use std::ops::Range; + +#[derive(Clone)] +pub struct Api; +pub struct Context; +pub struct Encoder; +#[derive(Debug)] +pub struct Resource; + +type DeviceResult = Result; + +impl crate::Api for Api { + type Instance = Context; + type Surface = Context; + type Adapter = Context; + type Device = Context; + + type Queue = Context; + type CommandEncoder = Encoder; + type CommandBuffer = Resource; + + type Buffer = Resource; + type Texture = Resource; + type SurfaceTexture = Resource; + type TextureView = Resource; + type Sampler = Resource; + type QuerySet = Resource; + type Fence = Resource; + + type BindGroupLayout = Resource; + type BindGroup = Resource; + type PipelineLayout = Resource; + type ShaderModule = Resource; + type RenderPipeline = Resource; + type ComputePipeline = Resource; +} + +impl crate::Instance for Context { + unsafe fn init(desc: &crate::InstanceDescriptor) -> Result { + Ok(Context) + } + unsafe fn create_surface( + &self, + rwh: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result { + Ok(Context) + } + unsafe fn destroy_surface(&self, surface: Context) {} + unsafe fn enumerate_adapters(&self) -> Vec> { + Vec::new() + } +} + +impl crate::Surface for Context { + unsafe fn configure( + &mut self, + device: &Context, + config: &crate::SurfaceConfiguration, + ) -> Result<(), crate::SurfaceError> { + Ok(()) + } + + unsafe fn unconfigure(&mut self, device: &Context) {} + + unsafe fn acquire_texture( + &mut self, + timeout_ms: u32, + ) -> Result>, crate::SurfaceError> { + Ok(None) + } + unsafe fn discard_texture(&mut self, texture: Resource) {} +} + +impl crate::Adapter for Context { + unsafe fn open(&self, features: wgt::Features) -> DeviceResult> { + Err(crate::DeviceError::Lost) + } + unsafe fn texture_format_capabilities( + &self, + format: wgt::TextureFormat, + ) -> crate::TextureFormatCapability { + crate::TextureFormatCapability::empty() + } + unsafe fn surface_capabilities(&self, surface: &Context) -> Option { + None + } +} + +impl crate::Queue for Context { + unsafe fn submit( + &mut self, + command_buffers: &[&Resource], + signal_fence: Option<(&mut Resource, crate::FenceValue)>, + ) -> DeviceResult<()> { + Ok(()) + } + unsafe fn present( + &mut self, + surface: &mut Context, + texture: Resource, + ) -> Result<(), crate::SurfaceError> { + Ok(()) + } +} + +impl crate::Device for Context { + unsafe fn exit(self) {} + unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_buffer(&self, buffer: Resource) {} + unsafe fn map_buffer( + &self, + buffer: &Resource, + range: crate::MemoryRange, + ) -> DeviceResult { + Err(crate::DeviceError::Lost) + } + unsafe fn unmap_buffer(&self, buffer: &Resource) -> DeviceResult<()> { + Ok(()) + } + unsafe fn flush_mapped_ranges(&self, buffer: &Resource, ranges: I) {} + unsafe fn invalidate_mapped_ranges(&self, buffer: &Resource, ranges: I) {} + + unsafe fn create_texture(&self, desc: &crate::TextureDescriptor) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_texture(&self, texture: Resource) {} + unsafe fn create_texture_view( + &self, + texture: &Resource, + desc: &crate::TextureViewDescriptor, + ) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_texture_view(&self, view: Resource) {} + unsafe fn create_sampler(&self, desc: &crate::SamplerDescriptor) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_sampler(&self, sampler: Resource) {} + + unsafe fn create_command_encoder( + &self, + desc: &crate::CommandEncoderDescriptor, + ) -> DeviceResult { + Ok(Encoder) + } + unsafe fn destroy_command_encoder(&self, encoder: Encoder) {} + + unsafe fn create_bind_group_layout( + &self, + desc: &crate::BindGroupLayoutDescriptor, + ) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_bind_group_layout(&self, bg_layout: Resource) {} + unsafe fn create_pipeline_layout( + &self, + desc: &crate::PipelineLayoutDescriptor, + ) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_pipeline_layout(&self, pipeline_layout: Resource) {} + unsafe fn create_bind_group( + &self, + desc: &crate::BindGroupDescriptor, + ) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_bind_group(&self, group: Resource) {} + + unsafe fn create_shader_module( + &self, + desc: &crate::ShaderModuleDescriptor, + shader: crate::NagaShader, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_shader_module(&self, module: Resource) {} + unsafe fn create_render_pipeline( + &self, + desc: &crate::RenderPipelineDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_render_pipeline(&self, pipeline: Resource) {} + unsafe fn create_compute_pipeline( + &self, + desc: &crate::ComputePipelineDescriptor, + ) -> Result { + Ok(Resource) + } + unsafe fn destroy_compute_pipeline(&self, pipeline: Resource) {} + + unsafe fn create_query_set( + &self, + desc: &wgt::QuerySetDescriptor, + ) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_query_set(&self, set: Resource) {} + unsafe fn create_fence(&self) -> DeviceResult { + Ok(Resource) + } + unsafe fn destroy_fence(&self, fence: Resource) {} + unsafe fn get_fence_value(&self, fence: &Resource) -> DeviceResult { + Ok(0) + } + unsafe fn wait( + &self, + fence: &Resource, + value: crate::FenceValue, + timeout_ms: u32, + ) -> DeviceResult { + Ok(true) + } + + unsafe fn start_capture(&self) -> bool { + false + } + unsafe fn stop_capture(&self) {} +} + +impl crate::CommandEncoder for Encoder { + unsafe fn begin_encoding(&mut self, label: crate::Label) -> DeviceResult<()> { + Ok(()) + } + unsafe fn discard_encoding(&mut self) {} + unsafe fn end_encoding(&mut self) -> DeviceResult { + Ok(Resource) + } + unsafe fn reset_all(&mut self, command_buffers: I) {} + + unsafe fn transition_buffers<'a, T>(&mut self, barriers: T) + where + T: Iterator>, + { + } + + unsafe fn transition_textures<'a, T>(&mut self, barriers: T) + where + T: Iterator>, + { + } + + unsafe fn fill_buffer(&mut self, buffer: &Resource, range: crate::MemoryRange, value: u8) {} + + unsafe fn copy_buffer_to_buffer(&mut self, src: &Resource, dst: &Resource, regions: T) {} + + unsafe fn copy_texture_to_texture( + &mut self, + src: &Resource, + src_usage: crate::TextureUse, + dst: &Resource, + regions: T, + ) { + } + + unsafe fn copy_buffer_to_texture(&mut self, src: &Resource, dst: &Resource, regions: T) {} + + unsafe fn copy_texture_to_buffer( + &mut self, + src: &Resource, + src_usage: crate::TextureUse, + dst: &Resource, + regions: T, + ) { + } + + unsafe fn begin_query(&mut self, set: &Resource, index: u32) {} + unsafe fn end_query(&mut self, set: &Resource, index: u32) {} + unsafe fn write_timestamp(&mut self, set: &Resource, index: u32) {} + unsafe fn reset_queries(&mut self, set: &Resource, range: Range) {} + unsafe fn copy_query_results( + &mut self, + set: &Resource, + range: Range, + buffer: &Resource, + offset: wgt::BufferAddress, + ) { + } + + // render + + unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor) {} + unsafe fn end_render_pass(&mut self) {} + + unsafe fn set_bind_group( + &mut self, + layout: &Resource, + index: u32, + group: &Resource, + dynamic_offsets: &[wgt::DynamicOffset], + ) { + } + unsafe fn set_push_constants( + &mut self, + layout: &Resource, + stages: wgt::ShaderStage, + offset: u32, + data: &[u32], + ) { + } + + unsafe fn insert_debug_marker(&mut self, label: &str) {} + unsafe fn begin_debug_marker(&mut self, group_label: &str) {} + unsafe fn end_debug_marker(&mut self) {} + + unsafe fn set_render_pipeline(&mut self, pipeline: &Resource) {} + + unsafe fn set_index_buffer<'a>( + &mut self, + binding: crate::BufferBinding<'a, Api>, + format: wgt::IndexFormat, + ) { + } + unsafe fn set_vertex_buffer<'a>(&mut self, index: u32, binding: crate::BufferBinding<'a, Api>) { + } + unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth_range: Range) {} + unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) {} + unsafe fn set_stencil_reference(&mut self, value: u32) {} + unsafe fn set_blend_constants(&mut self, color: &wgt::Color) {} + + unsafe fn draw( + &mut self, + start_vertex: u32, + vertex_count: u32, + start_instance: u32, + instance_count: u32, + ) { + } + unsafe fn draw_indexed( + &mut self, + start_index: u32, + index_count: u32, + base_vertex: i32, + start_instance: u32, + instance_count: u32, + ) { + } + unsafe fn draw_indirect( + &mut self, + buffer: &Resource, + offset: wgt::BufferAddress, + draw_count: u32, + ) { + } + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &Resource, + offset: wgt::BufferAddress, + draw_count: u32, + ) { + } + unsafe fn draw_indirect_count( + &mut self, + buffer: &Resource, + offset: wgt::BufferAddress, + count_buffer: &Resource, + count_offset: wgt::BufferAddress, + max_count: u32, + ) { + } + unsafe fn draw_indexed_indirect_count( + &mut self, + buffer: &Resource, + offset: wgt::BufferAddress, + count_buffer: &Resource, + count_offset: wgt::BufferAddress, + max_count: u32, + ) { + } + + // compute + + unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor) {} + unsafe fn end_compute_pass(&mut self) {} + + unsafe fn set_compute_pipeline(&mut self, pipeline: &Resource) {} + + unsafe fn dispatch(&mut self, count: [u32; 3]) {} + unsafe fn dispatch_indirect(&mut self, buffer: &Resource, offset: wgt::BufferAddress) {} +} diff --git a/wgpu-hal/src/lib.rs b/wgpu-hal/src/lib.rs new file mode 100644 index 0000000000..97e3c7c431 --- /dev/null +++ b/wgpu-hal/src/lib.rs @@ -0,0 +1,1061 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/*! This library describes the internal unsafe graphics abstraction API. + * It follows WebGPU for the most part, re-using wgpu-types, + * with the following deviations: + * - Fully unsafe: zero overhead, zero validation. + * - Compile-time backend selection via traits. + * - Objects are passed by references and returned by value. No IDs. + * - Mapping is persistent, with explicit synchronization. + * - Resource transitions are explicit. + * - All layouts are explicit. Binding model has compatibility. + * + * General design direction is to follow the majority by the following weights: + * - wgpu-core: 1.5 + * - primary backends (Vulkan/Metal/DX12): 1.0 each + * - secondary backends (DX11/GLES): 0.5 each + */ + +#![allow( + // We use loops for getting early-out of scope without closures. + clippy::never_loop, + // We don't use syntax sugar where it's not necessary. + clippy::match_like_matches_macro, + // Redundant matching is more explicit. + clippy::redundant_pattern_matching, + // Explicit lifetimes are often easier to reason about. + clippy::needless_lifetimes, + // No need for defaults in the internal types. + clippy::new_without_default, + // Matches are good and extendable, no need to make an exception here. + clippy::single_match, + // Push commands are more regular than macros. + clippy::vec_init_then_push, + // TODO! + clippy::missing_safety_doc, +)] +#![warn( + trivial_casts, + trivial_numeric_casts, + unused_extern_crates, + unused_qualifications, + // We don't match on a reference, unless required. + clippy::pattern_type_mismatch, +)] + +mod empty; +#[cfg(feature = "metal")] +mod metal; +#[cfg(feature = "vulkan")] +mod vulkan; + +pub mod util; +pub mod api { + pub use super::empty::Api as Empty; + #[cfg(feature = "metal")] + pub use super::metal::Api as Metal; + #[cfg(feature = "vulkan")] + pub use super::vulkan::Api as Vulkan; +} + +use std::{ + borrow::Borrow, + fmt, + num::NonZeroU8, + ops::{Range, RangeInclusive}, + ptr::NonNull, +}; + +use bitflags::bitflags; +use thiserror::Error; + +pub const MAX_ANISOTROPY: u8 = 16; +pub const MAX_BIND_GROUPS: usize = 8; +pub const MAX_VERTEX_BUFFERS: usize = 16; +pub const MAX_COLOR_TARGETS: usize = 4; +pub const MAX_MIP_LEVELS: u32 = 16; +/// Size of a single occlusion/timestamp query, when copied into a buffer, in bytes. +pub const QUERY_SIZE: wgt::BufferAddress = 8; + +pub type Label<'a> = Option<&'a str>; +pub type MemoryRange = Range; +pub type FenceValue = u64; + +#[derive(Clone, Debug, PartialEq, Error)] +pub enum DeviceError { + #[error("out of memory")] + OutOfMemory, + #[error("device is lost")] + Lost, +} + +#[derive(Clone, Debug, PartialEq, Error)] +pub enum ShaderError { + #[error("compilation failed: {0:?}")] + Compilation(String), + #[error(transparent)] + Device(#[from] DeviceError), +} + +#[derive(Clone, Debug, PartialEq, Error)] +pub enum PipelineError { + #[error("linkage failed for stage {0:?}: {1}")] + Linkage(wgt::ShaderStage, String), + #[error("entry point for stage {0:?} is invalid")] + EntryPoint(naga::ShaderStage), + #[error(transparent)] + Device(#[from] DeviceError), +} + +#[derive(Clone, Debug, PartialEq, Error)] +pub enum SurfaceError { + #[error("surface is lost")] + Lost, + #[error("surface is outdated, needs to be re-created")] + Outdated, + #[error(transparent)] + Device(#[from] DeviceError), + #[error("other reason: {0}")] + Other(&'static str), +} + +#[derive(Clone, Debug, PartialEq, Error)] +#[error("Not supported")] +pub struct InstanceError; + +pub trait Api: Clone + Sized { + type Instance: Instance; + type Surface: Surface; + type Adapter: Adapter; + type Device: Device; + + type Queue: Queue; + type CommandEncoder: CommandEncoder; + type CommandBuffer: Send + Sync; + + type Buffer: fmt::Debug + Send + Sync + 'static; + type Texture: fmt::Debug + Send + Sync + 'static; + type SurfaceTexture: fmt::Debug + Send + Sync + Borrow; + type TextureView: fmt::Debug + Send + Sync; + type Sampler: fmt::Debug + Send + Sync; + type QuerySet: fmt::Debug + Send + Sync; + type Fence: fmt::Debug + Send + Sync; + + type BindGroupLayout: Send + Sync; + type BindGroup: fmt::Debug + Send + Sync; + type PipelineLayout: Send + Sync; + type ShaderModule: fmt::Debug + Send + Sync; + type RenderPipeline: Send + Sync; + type ComputePipeline: Send + Sync; +} + +pub trait Instance: Sized + Send + Sync { + unsafe fn init(desc: &InstanceDescriptor) -> Result; + unsafe fn create_surface( + &self, + rwh: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result; + unsafe fn destroy_surface(&self, surface: A::Surface); + unsafe fn enumerate_adapters(&self) -> Vec>; +} + +pub trait Surface: Send + Sync { + unsafe fn configure( + &mut self, + device: &A::Device, + config: &SurfaceConfiguration, + ) -> Result<(), SurfaceError>; + + unsafe fn unconfigure(&mut self, device: &A::Device); + + /// Returns `None` on timing out. + unsafe fn acquire_texture( + &mut self, + timeout_ms: u32, + ) -> Result>, SurfaceError>; + unsafe fn discard_texture(&mut self, texture: A::SurfaceTexture); +} + +pub trait Adapter: Send + Sync { + unsafe fn open(&self, features: wgt::Features) -> Result, DeviceError>; + + /// Return the set of supported capabilities for a texture format. + unsafe fn texture_format_capabilities( + &self, + format: wgt::TextureFormat, + ) -> TextureFormatCapability; + + /// Returns the capabilities of working with a specified surface. + /// + /// `None` means presentation is not supported for it. + unsafe fn surface_capabilities(&self, surface: &A::Surface) -> Option; +} + +pub trait Device: Send + Sync { + /// Exit connection to this logical device. + unsafe fn exit(self); + /// Creates a new buffer. + /// + /// The initial usage is `BufferUse::empty()`. + unsafe fn create_buffer(&self, desc: &BufferDescriptor) -> Result; + unsafe fn destroy_buffer(&self, buffer: A::Buffer); + //TODO: clarify if zero-sized mapping is allowed + unsafe fn map_buffer( + &self, + buffer: &A::Buffer, + range: MemoryRange, + ) -> Result; + unsafe fn unmap_buffer(&self, buffer: &A::Buffer) -> Result<(), DeviceError>; + unsafe fn flush_mapped_ranges(&self, buffer: &A::Buffer, ranges: I) + where + I: Iterator; + unsafe fn invalidate_mapped_ranges(&self, buffer: &A::Buffer, ranges: I) + where + I: Iterator; + + /// Creates a new texture. + /// + /// The initial usage for all subresources is `TextureUse::UNINITIALIZED`. + unsafe fn create_texture(&self, desc: &TextureDescriptor) -> Result; + unsafe fn destroy_texture(&self, texture: A::Texture); + unsafe fn create_texture_view( + &self, + texture: &A::Texture, + desc: &TextureViewDescriptor, + ) -> Result; + unsafe fn destroy_texture_view(&self, view: A::TextureView); + unsafe fn create_sampler(&self, desc: &SamplerDescriptor) -> Result; + unsafe fn destroy_sampler(&self, sampler: A::Sampler); + + unsafe fn create_command_encoder( + &self, + desc: &CommandEncoderDescriptor, + ) -> Result; + unsafe fn destroy_command_encoder(&self, pool: A::CommandEncoder); + + unsafe fn create_bind_group_layout( + &self, + desc: &BindGroupLayoutDescriptor, + ) -> Result; + unsafe fn destroy_bind_group_layout(&self, bg_layout: A::BindGroupLayout); + unsafe fn create_pipeline_layout( + &self, + desc: &PipelineLayoutDescriptor, + ) -> Result; + unsafe fn destroy_pipeline_layout(&self, pipeline_layout: A::PipelineLayout); + unsafe fn create_bind_group( + &self, + desc: &BindGroupDescriptor, + ) -> Result; + unsafe fn destroy_bind_group(&self, group: A::BindGroup); + + unsafe fn create_shader_module( + &self, + desc: &ShaderModuleDescriptor, + shader: NagaShader, + ) -> Result; + unsafe fn destroy_shader_module(&self, module: A::ShaderModule); + unsafe fn create_render_pipeline( + &self, + desc: &RenderPipelineDescriptor, + ) -> Result; + unsafe fn destroy_render_pipeline(&self, pipeline: A::RenderPipeline); + unsafe fn create_compute_pipeline( + &self, + desc: &ComputePipelineDescriptor, + ) -> Result; + unsafe fn destroy_compute_pipeline(&self, pipeline: A::ComputePipeline); + + unsafe fn create_query_set( + &self, + desc: &wgt::QuerySetDescriptor); + unsafe fn end_render_pass(&mut self); + + unsafe fn set_render_pipeline(&mut self, pipeline: &A::RenderPipeline); + + unsafe fn set_index_buffer<'a>( + &mut self, + binding: BufferBinding<'a, A>, + format: wgt::IndexFormat, + ); + unsafe fn set_vertex_buffer<'a>(&mut self, index: u32, binding: BufferBinding<'a, A>); + unsafe fn set_viewport(&mut self, rect: &Rect, depth_range: Range); + unsafe fn set_scissor_rect(&mut self, rect: &Rect); + unsafe fn set_stencil_reference(&mut self, value: u32); + unsafe fn set_blend_constants(&mut self, color: &wgt::Color); + + unsafe fn draw( + &mut self, + start_vertex: u32, + vertex_count: u32, + start_instance: u32, + instance_count: u32, + ); + unsafe fn draw_indexed( + &mut self, + start_index: u32, + index_count: u32, + base_vertex: i32, + start_instance: u32, + instance_count: u32, + ); + unsafe fn draw_indirect( + &mut self, + buffer: &A::Buffer, + offset: wgt::BufferAddress, + draw_count: u32, + ); + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &A::Buffer, + offset: wgt::BufferAddress, + draw_count: u32, + ); + unsafe fn draw_indirect_count( + &mut self, + buffer: &A::Buffer, + offset: wgt::BufferAddress, + count_buffer: &A::Buffer, + count_offset: wgt::BufferAddress, + max_count: u32, + ); + unsafe fn draw_indexed_indirect_count( + &mut self, + buffer: &A::Buffer, + offset: wgt::BufferAddress, + count_buffer: &A::Buffer, + count_offset: wgt::BufferAddress, + max_count: u32, + ); + + // compute passes + + // Begins a compute pass, clears all active bindings. + unsafe fn begin_compute_pass(&mut self, desc: &ComputePassDescriptor); + unsafe fn end_compute_pass(&mut self); + + unsafe fn set_compute_pipeline(&mut self, pipeline: &A::ComputePipeline); + + unsafe fn dispatch(&mut self, count: [u32; 3]); + unsafe fn dispatch_indirect(&mut self, buffer: &A::Buffer, offset: wgt::BufferAddress); +} + +bitflags!( + /// Instance initialization flags. + pub struct InstanceFlag: u32 { + /// Generate debug information in shaders and objects. + const DEBUG = 0x1; + /// Enable validation, if possible. + const VALIDATION = 0x2; + } +); + +bitflags!( + /// Texture format capability flags. + pub struct TextureFormatCapability: u32 { + /// Format can be sampled. + const SAMPLED = 0x1; + /// Format can be sampled with a linear sampler. + const SAMPLED_LINEAR = 0x2; + /// Format can be sampled with a min/max reduction sampler. + const SAMPLED_MINMAX = 0x4; + + /// Format can be used as storage with exclusive read & write access. + const STORAGE = 0x10; + /// Format can be used as storage with simultaneous read/write access. + const STORAGE_READ_WRITE = 0x20; + /// Format can be used as storage with atomics. + const STORAGE_ATOMIC = 0x40; + + /// Format can be used as color and input attachment. + const COLOR_ATTACHMENT = 0x100; + /// Format can be used as color (with blending) and input attachment. + const COLOR_ATTACHMENT_BLEND = 0x200; + /// Format can be used as depth-stencil and input attachment. + const DEPTH_STENCIL_ATTACHMENT = 0x400; + + /// Format can be copied from. + const COPY_SRC = 0x1000; + /// Format can be copied to. + const COPY_DST = 0x2000; + } +); + +bitflags!( + /// Texture format capability flags. + pub struct FormatAspect: u8 { + const COLOR = 1; + const DEPTH = 2; + const STENCIL = 4; + } +); + +impl From for FormatAspect { + fn from(aspect: wgt::TextureAspect) -> Self { + match aspect { + wgt::TextureAspect::All => Self::all(), + wgt::TextureAspect::DepthOnly => Self::DEPTH, + wgt::TextureAspect::StencilOnly => Self::STENCIL, + } + } +} + +impl From for FormatAspect { + fn from(format: wgt::TextureFormat) -> Self { + match format { + wgt::TextureFormat::Depth32Float | wgt::TextureFormat::Depth24Plus => Self::DEPTH, + wgt::TextureFormat::Depth24PlusStencil8 => Self::DEPTH | Self::STENCIL, + _ => Self::COLOR, + } + } +} + +bitflags!( + pub struct MemoryFlag: u32 { + const TRANSIENT = 1; + } +); + +bitflags!( + pub struct AttachmentOp: u8 { + const LOAD = 1; + const STORE = 2; + } +); + +bitflags::bitflags! { + /// Similar to `wgt::BufferUsage` but for internal use. + pub struct BufferUse: u32 { + const MAP_READ = 1; + const MAP_WRITE = 2; + const COPY_SRC = 4; + const COPY_DST = 8; + const INDEX = 16; + const VERTEX = 32; + const UNIFORM = 64; + const STORAGE_LOAD = 128; + const STORAGE_STORE = 256; + const INDIRECT = 512; + /// The combination of all read-only usages. + const READ_ALL = Self::MAP_READ.bits | Self::COPY_SRC.bits | + Self::INDEX.bits | Self::VERTEX.bits | Self::UNIFORM.bits | + Self::STORAGE_LOAD.bits | Self::INDIRECT.bits; + /// The combination of all write-only and read-write usages. + const WRITE_ALL = Self::MAP_WRITE.bits | Self::COPY_DST.bits | Self::STORAGE_STORE.bits; + /// The combination of all usages that the are guaranteed to be be ordered by the hardware. + /// If a usage is not ordered, then even if it doesn't change between draw calls, there + /// still need to be pipeline barriers inserted for synchronization. + const ORDERED = Self::READ_ALL.bits | Self::MAP_WRITE.bits | Self::COPY_DST.bits; + } +} + +bitflags::bitflags! { + /// Similar to `wgt::TextureUsage` but for internal use. + pub struct TextureUse: u32 { + const COPY_SRC = 1; + const COPY_DST = 2; + const SAMPLED = 4; + const COLOR_TARGET = 8; + const DEPTH_STENCIL_READ = 16; + const DEPTH_STENCIL_WRITE = 32; + const STORAGE_LOAD = 64; + const STORAGE_STORE = 128; + /// The combination of all read-only usages. + const READ_ALL = Self::COPY_SRC.bits | Self::SAMPLED.bits | Self::DEPTH_STENCIL_READ.bits | Self::STORAGE_LOAD.bits; + /// The combination of all write-only and read-write usages. + const WRITE_ALL = Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits | Self::STORAGE_STORE.bits; + /// The combination of all usages that the are guaranteed to be be ordered by the hardware. + /// If a usage is not ordered, then even if it doesn't change between draw calls, there + /// still need to be pipeline barriers inserted for synchronization. + const ORDERED = Self::READ_ALL.bits | Self::COPY_DST.bits | Self::COLOR_TARGET.bits | Self::DEPTH_STENCIL_WRITE.bits; + const UNINITIALIZED = 0xFFFF; + } +} + +#[derive(Clone, Debug)] +pub struct InstanceDescriptor<'a> { + pub name: &'a str, + pub flags: InstanceFlag, +} + +#[derive(Clone, Debug)] +pub struct Alignments { + /// The alignment of the start of the buffer used as a GPU copy source. + pub buffer_copy_offset: wgt::BufferSize, + /// The alignment of the row pitch of the texture data stored in a buffer that is + /// used in a GPU copy operation. + pub buffer_copy_pitch: wgt::BufferSize, + pub storage_buffer_offset: wgt::BufferSize, + pub uniform_buffer_offset: wgt::BufferSize, +} + +#[derive(Clone, Debug)] +pub struct Capabilities { + pub limits: wgt::Limits, + pub alignments: Alignments, + pub downlevel: wgt::DownlevelCapabilities, +} + +#[derive(Debug)] +pub struct ExposedAdapter { + pub adapter: A::Adapter, + pub info: wgt::AdapterInfo, + pub features: wgt::Features, + pub capabilities: Capabilities, +} + +/// Describes information about what a `Surface`'s presentation capabilities are. +/// Fetch this with [Adapter::surface_capabilities]. +#[derive(Debug, Clone)] +pub struct SurfaceCapabilities { + /// List of supported texture formats. + /// + /// Must be at least one. + pub formats: Vec, + + /// Range for the swap chain sizes. + /// + /// - `swap_chain_sizes.start` must be at least 1. + /// - `swap_chain_sizes.end` must be larger or equal to `swap_chain_sizes.start`. + pub swap_chain_sizes: RangeInclusive, + + /// Current extent of the surface, if known. + pub current_extent: Option, + + /// Range of supported extents. + /// + /// `current_extent` must be inside this range. + pub extents: RangeInclusive, + + /// Supported texture usage flags. + /// + /// Must have at least `TextureUse::COLOR_TARGET` + pub usage: TextureUse, + + /// List of supported V-sync modes. + /// + /// Must be at least one. + pub present_modes: Vec, + + /// List of supported alpha composition modes. + /// + /// Must be at least one. + pub composite_alpha_modes: Vec, +} + +#[derive(Debug)] +pub struct AcquiredSurfaceTexture { + pub texture: A::SurfaceTexture, + /// The presentation configuration no longer matches + /// the surface properties exactly, but can still be used to present + /// to the surface successfully. + pub suboptimal: bool, +} + +#[derive(Debug)] +pub struct OpenDevice { + pub device: A::Device, + pub queue: A::Queue, +} + +#[derive(Clone, Debug)] +pub struct BufferMapping { + pub ptr: NonNull, + pub is_coherent: bool, +} + +#[derive(Clone, Debug)] +pub struct BufferDescriptor<'a> { + pub label: Label<'a>, + pub size: wgt::BufferAddress, + pub usage: BufferUse, + pub memory_flags: MemoryFlag, +} + +#[derive(Clone, Debug)] +pub struct TextureDescriptor<'a> { + pub label: Label<'a>, + pub size: wgt::Extent3d, + pub mip_level_count: u32, + pub sample_count: u32, + pub dimension: wgt::TextureDimension, + pub format: wgt::TextureFormat, + pub usage: TextureUse, + pub memory_flags: MemoryFlag, +} + +/// TextureView descriptor. +/// +/// Valid usage: +///. - `format` has to be the same as `TextureDescriptor::format` +///. - `dimension` has to be compatible with `TextureDescriptor::dimension` +///. - `usage` has to be a subset of `TextureDescriptor::usage` +///. - `range` has to be a subset of parent texture +#[derive(Clone, Debug)] +pub struct TextureViewDescriptor<'a> { + pub label: Label<'a>, + pub format: wgt::TextureFormat, + pub dimension: wgt::TextureViewDimension, + pub usage: TextureUse, + pub range: wgt::ImageSubresourceRange, +} + +#[derive(Clone, Debug)] +pub struct SamplerDescriptor<'a> { + pub label: Label<'a>, + pub address_modes: [wgt::AddressMode; 3], + pub mag_filter: wgt::FilterMode, + pub min_filter: wgt::FilterMode, + pub mipmap_filter: wgt::FilterMode, + pub lod_clamp: Option>, + pub compare: Option, + pub anisotropy_clamp: Option, + pub border_color: Option, +} + +#[derive(Clone, Debug)] +pub struct BindGroupLayoutDescriptor<'a> { + pub label: Label<'a>, + pub entries: &'a [wgt::BindGroupLayoutEntry], +} + +#[derive(Clone, Debug)] +pub struct PipelineLayoutDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub bind_group_layouts: &'a [&'a A::BindGroupLayout], + pub push_constant_ranges: &'a [wgt::PushConstantRange], +} + +#[derive(Debug)] +pub struct BufferBinding<'a, A: Api> { + pub buffer: &'a A::Buffer, + pub offset: wgt::BufferAddress, + pub size: Option, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for BufferBinding<'_, A> { + fn clone(&self) -> Self { + Self { + buffer: self.buffer, + offset: self.offset, + size: self.size, + } + } +} + +#[derive(Debug)] +pub struct TextureBinding<'a, A: Api> { + pub view: &'a A::TextureView, + pub usage: TextureUse, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for TextureBinding<'_, A> { + fn clone(&self) -> Self { + Self { + view: self.view, + usage: self.usage, + } + } +} + +#[derive(Clone, Debug)] +pub struct BindGroupEntry { + pub binding: u32, + pub resource_index: u32, +} + +/// BindGroup descriptor. +/// +/// Valid usage: +///. - `entries` has to be sorted by ascending `BindGroupEntry::binding` +///. - `entries` has to have the same set of `BindGroupEntry::binding` as `layout` +///. - each entry has to be compatible with the `layout` +///. - each entry's `BindGroupEntry::resource_index` is within range +/// of the corresponding resource array, selected by the relevant +/// `BindGroupLayoutEntry`. +#[derive(Clone, Debug)] +pub struct BindGroupDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub layout: &'a A::BindGroupLayout, + pub buffers: &'a [BufferBinding<'a, A>], + pub samplers: &'a [&'a A::Sampler], + pub textures: &'a [TextureBinding<'a, A>], + pub entries: &'a [BindGroupEntry], +} + +#[derive(Clone, Debug)] +pub struct CommandEncoderDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub queue: &'a A::Queue, +} + +/// Naga shader module. +#[derive(Debug)] +pub struct NagaShader { + /// Shader module IR. + pub module: naga::Module, + /// Analysis information of the module. + pub info: naga::valid::ModuleInfo, +} + +pub struct ShaderModuleDescriptor<'a> { + pub label: Label<'a>, +} + +/// Describes a programmable pipeline stage. +#[derive(Debug)] +pub struct ProgrammableStage<'a, A: Api> { + /// The compiled shader module for this stage. + pub module: &'a A::ShaderModule, + /// The name of the entry point in the compiled shader. There must be a function that returns + /// void with this name in the shader. + pub entry_point: &'a str, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for ProgrammableStage<'_, A> { + fn clone(&self) -> Self { + Self { + module: self.module, + entry_point: self.entry_point, + } + } +} + +/// Describes a compute pipeline. +#[derive(Clone, Debug)] +pub struct ComputePipelineDescriptor<'a, A: Api> { + pub label: Label<'a>, + /// The layout of bind groups for this pipeline. + pub layout: &'a A::PipelineLayout, + /// The compiled compute stage and its entry point. + pub stage: ProgrammableStage<'a, A>, +} + +/// Describes how the vertex buffer is interpreted. +#[derive(Clone, Debug)] +pub struct VertexBufferLayout<'a> { + /// The stride, in bytes, between elements of this buffer. + pub array_stride: wgt::BufferAddress, + /// How often this vertex buffer is "stepped" forward. + pub step_mode: wgt::InputStepMode, + /// The list of attributes which comprise a single vertex. + pub attributes: &'a [wgt::VertexAttribute], +} + +/// Describes a render (graphics) pipeline. +#[derive(Clone, Debug)] +pub struct RenderPipelineDescriptor<'a, A: Api> { + pub label: Label<'a>, + /// The layout of bind groups for this pipeline. + pub layout: &'a A::PipelineLayout, + /// The format of any vertex buffers used with this pipeline. + pub vertex_buffers: &'a [VertexBufferLayout<'a>], + /// The vertex stage for this pipeline. + pub vertex_stage: ProgrammableStage<'a, A>, + /// The properties of the pipeline at the primitive assembly and rasterization level. + pub primitive: wgt::PrimitiveState, + /// The effect of draw calls on the depth and stencil aspects of the output target, if any. + pub depth_stencil: Option, + /// The multi-sampling properties of the pipeline. + pub multisample: wgt::MultisampleState, + /// The fragment stage for this pipeline. + pub fragment_stage: Option>, + /// The effect of draw calls on the color aspect of the output target. + pub color_targets: &'a [wgt::ColorTargetState], +} + +/// Specifies how the alpha channel of the textures should be handled during (martin mouv i step) +/// compositing. +#[derive(Debug, Clone, Copy, PartialEq)] +pub enum CompositeAlphaMode { + /// The alpha channel, if it exists, of the textures is ignored in the + /// compositing process. Instead, the textures is treated as if it has a + /// constant alpha of 1.0. + Opaque, + /// The alpha channel, if it exists, of the textures is respected in the + /// compositing process. The non-alpha channels of the textures are + /// expected to already be multiplied by the alpha channel by the + /// application. + PreMultiplied, + /// The alpha channel, if it exists, of the textures is respected in the + /// compositing process. The non-alpha channels of the textures are not + /// expected to already be multiplied by the alpha channel by the + /// application; instead, the compositor will multiply the non-alpha + /// channels of the texture by the alpha channel during compositing. + PostMultiplied, +} + +#[derive(Debug, Clone)] +pub struct SurfaceConfiguration { + /// Number of textures in the swap chain. Must be in + /// `SurfaceCapabilities::swap_chain_size` range. + pub swap_chain_size: u32, + /// Vertical synchronization mode. + pub present_mode: wgt::PresentMode, + /// Alpha composition mode. + pub composite_alpha_mode: CompositeAlphaMode, + /// Format of the surface textures. + pub format: wgt::TextureFormat, + /// Requested texture extent. Must be in + /// `SurfaceCapabilities::extents` range. + pub extent: wgt::Extent3d, + /// Allowed usage of surface textures, + pub usage: TextureUse, +} + +#[derive(Debug, Clone)] +pub struct Rect { + pub x: T, + pub y: T, + pub w: T, + pub h: T, +} + +#[derive(Debug, Clone)] +pub struct BufferBarrier<'a, A: Api> { + pub buffer: &'a A::Buffer, + pub usage: Range, +} + +#[derive(Debug, Clone)] +pub struct TextureBarrier<'a, A: Api> { + pub texture: &'a A::Texture, + pub range: wgt::ImageSubresourceRange, + pub usage: Range, +} + +#[derive(Clone, Copy, Debug)] +pub struct BufferCopy { + pub src_offset: wgt::BufferAddress, + pub dst_offset: wgt::BufferAddress, + pub size: wgt::BufferSize, +} + +#[derive(Clone, Debug)] +pub struct TextureCopyBase { + pub origin: wgt::Origin3d, + pub mip_level: u32, + pub aspect: FormatAspect, +} + +#[derive(Clone, Debug)] +pub struct TextureCopy { + pub src_base: TextureCopyBase, + pub dst_base: TextureCopyBase, + pub size: wgt::Extent3d, +} + +#[derive(Clone, Debug)] +pub struct BufferTextureCopy { + pub buffer_layout: wgt::ImageDataLayout, + pub texture_base: TextureCopyBase, + pub size: wgt::Extent3d, +} + +#[derive(Debug)] +pub struct Attachment<'a, A: Api> { + pub view: &'a A::TextureView, + /// Contains either a single mutating usage as a target, or a valid combination + /// of read-only usages. + pub usage: TextureUse, + /// Defines the boundary usages for the attachment. + /// It is expected to begin a render pass with `boundary_usage.start` usage, + /// and will end it with `boundary_usage.end` usage. + pub boundary_usage: Range, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for Attachment<'_, A> { + fn clone(&self) -> Self { + Self { + view: self.view, + usage: self.usage, + boundary_usage: self.boundary_usage.clone(), + } + } +} + +#[derive(Debug)] +pub struct ColorAttachment<'a, A: Api> { + pub target: Attachment<'a, A>, + pub resolve_target: Option>, + pub ops: AttachmentOp, + pub clear_value: wgt::Color, +} + +// Rust gets confused about the impl requirements for `A` +impl Clone for ColorAttachment<'_, A> { + fn clone(&self) -> Self { + Self { + target: self.target.clone(), + resolve_target: self.resolve_target.clone(), + ops: self.ops, + clear_value: self.clear_value, + } + } +} + +#[derive(Clone, Debug)] +pub struct DepthStencilAttachment<'a, A: Api> { + pub target: Attachment<'a, A>, + pub depth_ops: AttachmentOp, + pub stencil_ops: AttachmentOp, + pub clear_value: (f32, u32), +} + +#[derive(Clone, Debug)] +pub struct RenderPassDescriptor<'a, A: Api> { + pub label: Label<'a>, + pub color_attachments: &'a [ColorAttachment<'a, A>], + pub depth_stencil_attachment: Option>, +} + +#[derive(Clone, Debug)] +pub struct ComputePassDescriptor<'a> { + pub label: Label<'a>, +} + +#[test] +fn test_default_limits() { + let limits = wgt::Limits::default(); + assert!(limits.max_bind_groups <= MAX_BIND_GROUPS as u32); +} diff --git a/wgpu-hal/src/metal/adapter.rs b/wgpu-hal/src/metal/adapter.rs new file mode 100644 index 0000000000..a1c49a3b24 --- /dev/null +++ b/wgpu-hal/src/metal/adapter.rs @@ -0,0 +1,1006 @@ +use mtl::{MTLFeatureSet, MTLGPUFamily, MTLLanguageVersion}; +use objc::{class, msg_send, sel, sel_impl}; +use parking_lot::Mutex; + +use std::{sync::Arc, thread}; + +unsafe impl Send for super::Adapter {} +unsafe impl Sync for super::Adapter {} + +impl super::Adapter { + pub(super) fn new(shared: Arc) -> Self { + Self { shared } + } +} + +impl crate::Adapter for super::Adapter { + unsafe fn open( + &self, + features: wgt::Features, + ) -> Result, crate::DeviceError> { + let queue = self.shared.device.lock().new_command_queue(); + Ok(crate::OpenDevice { + device: super::Device { + shared: Arc::clone(&self.shared), + features, + }, + queue: super::Queue { + raw: Arc::new(Mutex::new(queue)), + }, + }) + } + + unsafe fn texture_format_capabilities( + &self, + format: wgt::TextureFormat, + ) -> crate::TextureFormatCapability { + use crate::TextureFormatCapability as Tfc; + use wgt::TextureFormat as Tf; + + let pc = &self.shared.private_caps; + // Affected formats documented at: + // https://developer.apple.com/documentation/metal/mtlreadwritetexturetier/mtlreadwritetexturetier1?language=objc + // https://developer.apple.com/documentation/metal/mtlreadwritetexturetier/mtlreadwritetexturetier2?language=objc + let (read_write_tier1_if, read_write_tier2_if) = match pc.read_write_texture_tier { + mtl::MTLReadWriteTextureTier::TierNone => (Tfc::empty(), Tfc::empty()), + mtl::MTLReadWriteTextureTier::Tier1 => (Tfc::STORAGE_READ_WRITE, Tfc::empty()), + mtl::MTLReadWriteTextureTier::Tier2 => { + (Tfc::STORAGE_READ_WRITE, Tfc::STORAGE_READ_WRITE) + } + }; + + let extra = match format { + Tf::R8Unorm => { + read_write_tier2_if + | Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::R8Snorm => { + Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::R8Uint | Tf::R8Sint | Tf::R16Uint | Tf::R16Sint => { + read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT + } + Tf::R16Float => { + read_write_tier2_if + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::Rg8Unorm | Tf::Rg8Snorm => { + Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::Rg8Uint | Tf::Rg8Sint => Tfc::COLOR_ATTACHMENT, + Tf::R32Uint | Tf::R32Sint => { + if pc.format_r32_all { + read_write_tier1_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT + } else { + Tfc::COLOR_ATTACHMENT + } + } + Tf::R32Float => { + let mut flags = Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND; + if pc.format_r32float_all { + flags |= read_write_tier1_if | Tfc::STORAGE | Tfc::SAMPLED_LINEAR; + } else if pc.format_r32float_no_filter { + flags |= Tfc::SAMPLED_LINEAR; + } + flags + } + Tf::Rg16Uint | Tf::Rg16Sint => { + read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT + } + Tf::Rg16Float => { + read_write_tier2_if + | Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::Rgba8Unorm => { + read_write_tier2_if + | Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::Rgba8UnormSrgb | Tf::Bgra8UnormSrgb => { + let mut flags = + Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND; + flags.set(Tfc::STORAGE, pc.format_rgba8_srgb_all); + flags + } + Tf::Rgba8Snorm | Tf::Bgra8Unorm => { + Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::Rgba8Uint | Tf::Rgba8Sint => { + read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT + } + Tf::Rgb10a2Unorm => { + let mut flags = + Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND; + flags.set(Tfc::STORAGE, pc.format_rgb10a2_unorm_all); + flags + } + Tf::Rg11b10Float => { + let mut flags = + Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND; + flags.set(Tfc::STORAGE, pc.format_rg11b10_all); + flags + } + Tf::Rg32Uint | Tf::Rg32Sint => Tfc::COLOR_ATTACHMENT | Tfc::STORAGE, + Tf::Rg32Float => { + let mut flags = Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND; + if pc.format_rg32float_all { + flags |= Tfc::STORAGE | Tfc::SAMPLED_LINEAR; + } else if pc.format_rg32float_color_blend { + flags |= Tfc::SAMPLED_LINEAR; + } + flags + } + Tf::Rgba16Uint | Tf::Rgba16Sint => { + read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT + } + Tf::Rgba16Float => { + read_write_tier2_if + | Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } + Tf::Rgba32Uint | Tf::Rgba32Sint => { + if pc.format_rgba32int_color_write { + read_write_tier2_if | Tfc::COLOR_ATTACHMENT | Tfc::STORAGE + } else { + Tfc::COLOR_ATTACHMENT + } + } + Tf::Rgba32Float => { + if pc.format_rgba32float_all { + read_write_tier2_if + | Tfc::SAMPLED_LINEAR + | Tfc::STORAGE + | Tfc::COLOR_ATTACHMENT + | Tfc::COLOR_ATTACHMENT_BLEND + } else if pc.format_rgba32float_color_write { + read_write_tier2_if | Tfc::COLOR_ATTACHMENT | Tfc::STORAGE + } else { + Tfc::COLOR_ATTACHMENT + } + } + Tf::Depth32Float => { + if pc.format_depth32float_filter { + Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::SAMPLED_LINEAR + } else { + Tfc::DEPTH_STENCIL_ATTACHMENT + } + } + Tf::Depth24Plus | Tf::Depth24PlusStencil8 => { + Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::SAMPLED_LINEAR + } + Tf::Bc1RgbaUnorm + | Tf::Bc1RgbaUnormSrgb + | Tf::Bc2RgbaUnorm + | Tf::Bc2RgbaUnormSrgb + | Tf::Bc3RgbaUnorm + | Tf::Bc3RgbaUnormSrgb + | Tf::Bc4RUnorm + | Tf::Bc4RSnorm + | Tf::Bc5RgUnorm + | Tf::Bc5RgSnorm + | Tf::Bc6hRgbSfloat + | Tf::Bc6hRgbUfloat + | Tf::Bc7RgbaUnorm + | Tf::Bc7RgbaUnormSrgb => { + if pc.format_bc { + Tfc::SAMPLED_LINEAR + } else { + Tfc::empty() + } + } + Tf::Etc2RgbUnorm + | Tf::Etc2RgbUnormSrgb + | Tf::Etc2RgbA1Unorm + | Tf::Etc2RgbA1UnormSrgb + | Tf::EacRUnorm + | Tf::EacRSnorm + | Tf::EtcRgUnorm + | Tf::EtcRgSnorm => { + if pc.format_eac_etc { + Tfc::SAMPLED_LINEAR + } else { + Tfc::empty() + } + } + Tf::Astc4x4RgbaUnorm + | Tf::Astc4x4RgbaUnormSrgb + | Tf::Astc5x4RgbaUnorm + | Tf::Astc5x4RgbaUnormSrgb + | Tf::Astc5x5RgbaUnorm + | Tf::Astc5x5RgbaUnormSrgb + | Tf::Astc6x5RgbaUnorm + | Tf::Astc6x5RgbaUnormSrgb + | Tf::Astc6x6RgbaUnorm + | Tf::Astc6x6RgbaUnormSrgb + | Tf::Astc8x5RgbaUnorm + | Tf::Astc8x5RgbaUnormSrgb + | Tf::Astc8x6RgbaUnorm + | Tf::Astc8x6RgbaUnormSrgb + | Tf::Astc10x5RgbaUnorm + | Tf::Astc10x5RgbaUnormSrgb + | Tf::Astc10x6RgbaUnorm + | Tf::Astc10x6RgbaUnormSrgb + | Tf::Astc8x8RgbaUnorm + | Tf::Astc8x8RgbaUnormSrgb + | Tf::Astc10x8RgbaUnorm + | Tf::Astc10x8RgbaUnormSrgb + | Tf::Astc10x10RgbaUnorm + | Tf::Astc10x10RgbaUnormSrgb + | Tf::Astc12x10RgbaUnorm + | Tf::Astc12x10RgbaUnormSrgb + | Tf::Astc12x12RgbaUnorm + | Tf::Astc12x12RgbaUnormSrgb => { + if pc.format_astc { + Tfc::SAMPLED_LINEAR + } else { + Tfc::empty() + } + } + }; + + Tfc::COPY_SRC | Tfc::COPY_DST | Tfc::SAMPLED | extra + } + + unsafe fn surface_capabilities( + &self, + surface: &super::Surface, + ) -> Option { + let current_extent = if surface.main_thread_id == thread::current().id() { + Some(surface.dimensions()) + } else { + log::warn!("Unable to get the current view dimensions on a non-main thread"); + None + }; + + let pc = &self.shared.private_caps; + Some(crate::SurfaceCapabilities { + formats: vec![ + wgt::TextureFormat::Bgra8Unorm, + wgt::TextureFormat::Bgra8UnormSrgb, + wgt::TextureFormat::Rgba16Float, + ], + //Note: this is hardcoded in `CAMetalLayer` documentation + swap_chain_sizes: if pc.can_set_maximum_drawables_count { + 2..=3 + } else { + // 3 is the default in `CAMetalLayer` documentation + // iOS 10.3 was tested to use 3 on iphone5s + 3..=3 + }, + present_modes: if pc.can_set_display_sync { + vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate] + } else { + vec![wgt::PresentMode::Fifo] + }, + composite_alpha_modes: vec![ + crate::CompositeAlphaMode::Opaque, + crate::CompositeAlphaMode::PreMultiplied, + crate::CompositeAlphaMode::PostMultiplied, + ], + + current_extent, + extents: wgt::Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }..=wgt::Extent3d { + width: 4096, + height: 4096, + depth_or_array_layers: 1, + }, + usage: crate::TextureUse::COLOR_TARGET, //TODO: expose more + }) + } +} + +const RESOURCE_HEAP_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily1_v3, + MTLFeatureSet::iOS_GPUFamily2_v3, + MTLFeatureSet::iOS_GPUFamily3_v2, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v2, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v3, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const ARGUMENT_BUFFER_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily1_v4, + MTLFeatureSet::iOS_GPUFamily2_v4, + MTLFeatureSet::iOS_GPUFamily3_v3, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v3, + MTLFeatureSet::macOS_GPUFamily1_v3, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const MUTABLE_COMPARISON_SAMPLER_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const SAMPLER_CLAMP_TO_BORDER_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::macOS_GPUFamily1_v2, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const ASTC_PIXEL_FORMAT_FEATURES: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily2_v1, + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, +]; + +const ANY8_UNORM_SRGB_ALL: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily2_v3, + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v2, + MTLFeatureSet::tvOS_GPUFamily2_v1, +]; + +const ANY8_SNORM_RESOLVE: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily2_v1, + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const RGBA8_SRGB: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily2_v3, + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v2, + MTLFeatureSet::tvOS_GPUFamily2_v1, +]; + +const RGB10A2UNORM_ALL: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const RGB10A2UINT_COLOR_WRITE: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const RG11B10FLOAT_ALL: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const RGB9E5FLOAT_ALL: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, +]; + +const BGR10A2_ALL: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily1_v4, + MTLFeatureSet::iOS_GPUFamily2_v4, + MTLFeatureSet::iOS_GPUFamily3_v3, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v3, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v3, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const BASE_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const BASE_VERTEX_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const TEXTURE_CUBE_ARRAY_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v2, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const DUAL_SOURCE_BLEND_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily1_v4, + MTLFeatureSet::iOS_GPUFamily2_v4, + MTLFeatureSet::iOS_GPUFamily3_v3, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v3, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v2, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const LAYERED_RENDERING_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const FUNCTION_SPECIALIZATION_SUPPORT: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily1_v3, + MTLFeatureSet::iOS_GPUFamily2_v3, + MTLFeatureSet::iOS_GPUFamily3_v2, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v2, + MTLFeatureSet::macOS_GPUFamily1_v2, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +const DEPTH_CLIP_MODE: &[MTLFeatureSet] = &[ + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v3, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, +]; + +impl super::PrivateCapabilities { + fn version_at_least(major: u32, minor: u32, needed_major: u32, needed_minor: u32) -> bool { + major > needed_major || (major == needed_major && minor >= needed_minor) + } + + fn supports_any(raw: &mtl::DeviceRef, features_sets: &[MTLFeatureSet]) -> bool { + features_sets + .iter() + .cloned() + .any(|x| raw.supports_feature_set(x)) + } + + pub fn new(device: &mtl::Device) -> Self { + #[repr(C)] + #[derive(Clone, Copy, Debug)] + #[allow(clippy::upper_case_acronyms)] + struct NSOperatingSystemVersion { + major: usize, + minor: usize, + patch: usize, + } + + let version: NSOperatingSystemVersion = unsafe { + let process_info: *mut objc::runtime::Object = + msg_send![class!(NSProcessInfo), processInfo]; + msg_send![process_info, operatingSystemVersion] + }; + + let major = version.major as u32; + let minor = version.minor as u32; + let os_is_mac = device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1); + let family_check = if os_is_mac { + Self::version_at_least(major, minor, 10, 15) + } else { + Self::version_at_least(major, minor, 13, 0) + }; + + let mut sample_count_mask: u8 = 1 | 4; // 1 and 4 samples are supported on all devices + if device.supports_texture_sample_count(2) { + sample_count_mask |= 2; + } + if device.supports_texture_sample_count(8) { + sample_count_mask |= 8; + } + + Self { + family_check, + msl_version: if os_is_mac { + if Self::version_at_least(major, minor, 10, 15) { + MTLLanguageVersion::V2_2 + } else if Self::version_at_least(major, minor, 10, 14) { + MTLLanguageVersion::V2_1 + } else if Self::version_at_least(major, minor, 10, 13) { + MTLLanguageVersion::V2_0 + } else if Self::version_at_least(major, minor, 10, 12) { + MTLLanguageVersion::V1_2 + } else if Self::version_at_least(major, minor, 10, 11) { + MTLLanguageVersion::V1_1 + } else { + MTLLanguageVersion::V1_0 + } + } else if Self::version_at_least(major, minor, 13, 0) { + MTLLanguageVersion::V2_2 + } else if Self::version_at_least(major, minor, 12, 0) { + MTLLanguageVersion::V2_1 + } else if Self::version_at_least(major, minor, 11, 0) { + MTLLanguageVersion::V2_0 + } else if Self::version_at_least(major, minor, 10, 0) { + MTLLanguageVersion::V1_2 + } else if Self::version_at_least(major, minor, 9, 0) { + MTLLanguageVersion::V1_1 + } else { + MTLLanguageVersion::V1_0 + }, + exposed_queues: 1, + read_write_texture_tier: device.read_write_texture_support(), + resource_heaps: Self::supports_any(&device, RESOURCE_HEAP_SUPPORT), + argument_buffers: Self::supports_any(&device, ARGUMENT_BUFFER_SUPPORT), + shared_textures: !os_is_mac, + mutable_comparison_samplers: Self::supports_any( + &device, + MUTABLE_COMPARISON_SAMPLER_SUPPORT, + ), + sampler_clamp_to_border: Self::supports_any(&device, SAMPLER_CLAMP_TO_BORDER_SUPPORT), + sampler_lod_average: { + // TODO: Clarify minimum macOS version with Apple (43707452) + let need_version = if os_is_mac { (10, 13) } else { (9, 0) }; + Self::version_at_least(major, minor, need_version.0, need_version.1) + }, + base_instance: Self::supports_any(&device, BASE_INSTANCE_SUPPORT), + base_vertex_instance_drawing: Self::supports_any(&device, BASE_VERTEX_INSTANCE_SUPPORT), + dual_source_blending: Self::supports_any(&device, DUAL_SOURCE_BLEND_SUPPORT), + low_power: !os_is_mac || device.is_low_power(), + headless: os_is_mac && device.is_headless(), + layered_rendering: Self::supports_any(&device, LAYERED_RENDERING_SUPPORT), + function_specialization: Self::supports_any(&device, FUNCTION_SPECIALIZATION_SUPPORT), + depth_clip_mode: Self::supports_any(&device, DEPTH_CLIP_MODE), + texture_cube_array: Self::supports_any(&device, TEXTURE_CUBE_ARRAY_SUPPORT), + format_depth24_stencil8: os_is_mac && device.d24_s8_supported(), + format_depth32_stencil8_filter: os_is_mac, + format_depth32_stencil8_none: !os_is_mac, + format_min_srgb_channels: if os_is_mac { 4 } else { 1 }, + format_b5: !os_is_mac, + format_bc: os_is_mac, + format_eac_etc: !os_is_mac, + format_astc: Self::supports_any(&device, ASTC_PIXEL_FORMAT_FEATURES), + format_any8_unorm_srgb_all: Self::supports_any(&device, ANY8_UNORM_SRGB_ALL), + format_any8_unorm_srgb_no_write: !Self::supports_any(&device, ANY8_UNORM_SRGB_ALL) + && !os_is_mac, + format_any8_snorm_all: Self::supports_any(&device, ANY8_SNORM_RESOLVE), + format_r16_norm_all: os_is_mac, + format_r32_all: !Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_r32_no_write: Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_r32float_no_write_no_filter: Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ) && !os_is_mac, + format_r32float_no_filter: !Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ) && !os_is_mac, + format_r32float_all: os_is_mac, + format_rgba8_srgb_all: Self::supports_any(&device, RGBA8_SRGB), + format_rgba8_srgb_no_write: !Self::supports_any(&device, RGBA8_SRGB), + format_rgb10a2_unorm_all: Self::supports_any(&device, RGB10A2UNORM_ALL), + format_rgb10a2_unorm_no_write: !Self::supports_any(&device, RGB10A2UNORM_ALL), + format_rgb10a2_uint_color: !Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE), + format_rgb10a2_uint_color_write: Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE), + format_rg11b10_all: Self::supports_any(&device, RG11B10FLOAT_ALL), + format_rg11b10_no_write: !Self::supports_any(&device, RG11B10FLOAT_ALL), + format_rgb9e5_all: Self::supports_any(&device, RGB9E5FLOAT_ALL), + format_rgb9e5_no_write: !Self::supports_any(&device, RGB9E5FLOAT_ALL) && !os_is_mac, + format_rgb9e5_filter_only: os_is_mac, + format_rg32_color: Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rg32_color_write: !Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rg32float_all: os_is_mac, + format_rg32float_color_blend: Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rg32float_no_filter: !os_is_mac + && !Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rgba32int_color: Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rgba32int_color_write: !Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rgba32float_color: Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ), + format_rgba32float_color_write: !Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v1, + MTLFeatureSet::iOS_GPUFamily2_v1, + ], + ) && !os_is_mac, + format_rgba32float_all: os_is_mac, + format_depth16unorm: device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2), + format_depth32float_filter: device + .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1), + format_depth32float_none: !device + .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1), + format_bgr10a2_all: Self::supports_any(&device, BGR10A2_ALL), + format_bgr10a2_no_write: !device + .supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v3), + max_buffers_per_stage: 31, + max_textures_per_stage: if os_is_mac { 128 } else { 31 }, + max_samplers_per_stage: 16, + buffer_alignment: if os_is_mac { 256 } else { 64 }, + max_buffer_size: if device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2) { + 1 << 30 // 1GB on macOS 1.2 and up + } else { + 1 << 28 // 256MB otherwise + }, + max_texture_size: if Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + ], + ) { + 16384 + } else if Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily1_v2, + MTLFeatureSet::iOS_GPUFamily2_v2, + MTLFeatureSet::tvOS_GPUFamily1_v1, + ], + ) { + 8192 + } else { + 4096 + }, + max_texture_3d_size: 2048, + max_texture_layers: 2048, + max_fragment_input_components: if os_is_mac { 128 } else { 60 }, + max_color_render_targets: if Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily2_v1, + MTLFeatureSet::iOS_GPUFamily3_v1, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v1, + MTLFeatureSet::tvOS_GPUFamily2_v1, + MTLFeatureSet::macOS_GPUFamily1_v1, + MTLFeatureSet::macOS_GPUFamily2_v1, + ], + ) { + 8 + } else { + 4 + }, + max_total_threadgroup_memory: if Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily4_v2, + MTLFeatureSet::iOS_GPUFamily5_v1, + ], + ) { + 64 << 10 + } else if Self::supports_any( + &device, + &[ + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::macOS_GPUFamily1_v2, + MTLFeatureSet::macOS_GPUFamily2_v1, + ], + ) { + 32 << 10 + } else { + 16 << 10 + }, + sample_count_mask, + supports_debug_markers: Self::supports_any( + &device, + &[ + MTLFeatureSet::macOS_GPUFamily1_v2, + MTLFeatureSet::macOS_GPUFamily2_v1, + MTLFeatureSet::iOS_GPUFamily1_v3, + MTLFeatureSet::iOS_GPUFamily2_v3, + MTLFeatureSet::iOS_GPUFamily3_v2, + MTLFeatureSet::iOS_GPUFamily4_v1, + MTLFeatureSet::iOS_GPUFamily5_v1, + MTLFeatureSet::tvOS_GPUFamily1_v2, + MTLFeatureSet::tvOS_GPUFamily2_v1, + ], + ), + supports_binary_archives: family_check + && (device.supports_family(MTLGPUFamily::Apple3) + || device.supports_family(MTLGPUFamily::Mac1)), + supports_capture_manager: if os_is_mac { + Self::version_at_least(major, minor, 10, 13) + } else { + Self::version_at_least(major, minor, 11, 0) + }, + can_set_maximum_drawables_count: os_is_mac + || Self::version_at_least(major, minor, 11, 2), + can_set_display_sync: os_is_mac && Self::version_at_least(major, minor, 10, 13), + can_set_next_drawable_timeout: if os_is_mac { + Self::version_at_least(major, minor, 10, 13) + } else { + Self::version_at_least(major, minor, 11, 0) + }, + } + } + + pub fn features(&self) -> wgt::Features { + use wgt::Features as F; + + let mut features = F::empty() + | F::DEPTH_CLAMPING + | F::TEXTURE_COMPRESSION_BC + | F::MAPPABLE_PRIMARY_BUFFERS + | F::VERTEX_WRITABLE_STORAGE; + + features.set( + F::SAMPLED_TEXTURE_BINDING_ARRAY | F::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, + self.msl_version >= MTLLanguageVersion::V2_0, + ); + features.set( + F::ADDRESS_MODE_CLAMP_TO_BORDER, + self.sampler_clamp_to_border, + ); + + features + } + + pub fn capabilities(&self) -> crate::Capabilities { + let buffer_alignment = wgt::BufferSize::new(self.buffer_alignment).unwrap(); + let mut downlevel = wgt::DownlevelCapabilities::default(); + downlevel.flags.set( + wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES, + self.texture_cube_array, + ); + //TODO: separate the mutable comparisons from immutable ones + downlevel.flags.set( + wgt::DownlevelFlags::COMPARISON_SAMPLERS, + self.mutable_comparison_samplers, + ); + + crate::Capabilities { + limits: wgt::Limits { + max_texture_dimension_1d: self.max_texture_size as u32, + max_texture_dimension_2d: self.max_texture_size as u32, + max_texture_dimension_3d: self.max_texture_3d_size as u32, + max_texture_array_layers: self.max_texture_layers as u32, + max_bind_groups: 8, + max_dynamic_uniform_buffers_per_pipeline_layout: 8, + max_dynamic_storage_buffers_per_pipeline_layout: 4, + max_sampled_textures_per_shader_stage: 16, + max_samplers_per_shader_stage: self.max_samplers_per_stage, + max_storage_buffers_per_shader_stage: 4, + max_storage_textures_per_shader_stage: 4, + max_uniform_buffers_per_shader_stage: 12, + max_uniform_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32, + max_storage_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32, + max_vertex_buffers: 8, + max_vertex_attributes: 16, + max_vertex_buffer_array_stride: 2048, + max_push_constant_size: 0x1000, + }, + alignments: crate::Alignments { + buffer_copy_offset: buffer_alignment, + buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(), + storage_buffer_offset: buffer_alignment, + uniform_buffer_offset: buffer_alignment, + }, + downlevel, + } + } + + pub fn map_format(&self, format: wgt::TextureFormat) -> mtl::MTLPixelFormat { + use mtl::MTLPixelFormat::*; + use wgt::TextureFormat as Tf; + + match format { + Tf::R8Unorm => R8Unorm, + Tf::R8Snorm => R8Snorm, + Tf::R8Uint => R8Uint, + Tf::R8Sint => R8Sint, + Tf::R16Uint => R16Uint, + Tf::R16Sint => R16Sint, + Tf::R16Float => R16Float, + Tf::Rg8Unorm => RG8Unorm, + Tf::Rg8Snorm => RG8Snorm, + Tf::Rg8Uint => RG8Uint, + Tf::Rg8Sint => RG8Sint, + Tf::R32Uint => R32Uint, + Tf::R32Sint => R32Sint, + Tf::R32Float => R32Float, + Tf::Rg16Uint => RG16Uint, + Tf::Rg16Sint => RG16Sint, + Tf::Rg16Float => RG16Float, + Tf::Rgba8Unorm => RGBA8Unorm, + Tf::Rgba8UnormSrgb => RGBA8Unorm_sRGB, + Tf::Bgra8UnormSrgb => BGRA8Unorm_sRGB, + Tf::Rgba8Snorm => RGBA8Snorm, + Tf::Bgra8Unorm => BGRA8Unorm, + Tf::Rgba8Uint => RGBA8Uint, + Tf::Rgba8Sint => RGBA8Sint, + Tf::Rgb10a2Unorm => RGB10A2Unorm, + Tf::Rg11b10Float => RG11B10Float, + Tf::Rg32Uint => RG32Uint, + Tf::Rg32Sint => RG32Sint, + Tf::Rg32Float => RG32Float, + Tf::Rgba16Uint => RGBA16Uint, + Tf::Rgba16Sint => RGBA16Sint, + Tf::Rgba16Float => RGBA16Float, + Tf::Rgba32Uint => RGBA32Uint, + Tf::Rgba32Sint => RGBA32Sint, + Tf::Rgba32Float => RGBA32Float, + Tf::Depth32Float => Depth32Float, + Tf::Depth24Plus => { + if self.format_depth24_stencil8 { + Depth24Unorm_Stencil8 + } else { + Depth32Float + } + } + Tf::Depth24PlusStencil8 => { + if self.format_depth24_stencil8 { + Depth24Unorm_Stencil8 + } else { + Depth32Float_Stencil8 + } + } + Tf::Bc1RgbaUnorm => BC1_RGBA, + Tf::Bc1RgbaUnormSrgb => BC1_RGBA_sRGB, + Tf::Bc2RgbaUnorm => BC2_RGBA, + Tf::Bc2RgbaUnormSrgb => BC2_RGBA_sRGB, + Tf::Bc3RgbaUnorm => BC3_RGBA, + Tf::Bc3RgbaUnormSrgb => BC3_RGBA_sRGB, + Tf::Bc4RUnorm => BC4_RUnorm, + Tf::Bc4RSnorm => BC4_RSnorm, + Tf::Bc5RgUnorm => BC5_RGUnorm, + Tf::Bc5RgSnorm => BC5_RGSnorm, + Tf::Bc6hRgbSfloat => BC6H_RGBFloat, + Tf::Bc6hRgbUfloat => BC6H_RGBUfloat, + Tf::Bc7RgbaUnorm => BC7_RGBAUnorm, + Tf::Bc7RgbaUnormSrgb => BC7_RGBAUnorm_sRGB, + Tf::Etc2RgbUnorm => ETC2_RGB8, + Tf::Etc2RgbUnormSrgb => ETC2_RGB8_sRGB, + Tf::Etc2RgbA1Unorm => ETC2_RGB8A1, + Tf::Etc2RgbA1UnormSrgb => ETC2_RGB8A1_sRGB, + Tf::EacRUnorm => EAC_R11Unorm, + Tf::EacRSnorm => EAC_R11Snorm, + Tf::EtcRgUnorm => EAC_RG11Unorm, + Tf::EtcRgSnorm => EAC_RG11Snorm, + Tf::Astc4x4RgbaUnorm => ASTC_4x4_LDR, + Tf::Astc4x4RgbaUnormSrgb => ASTC_4x4_sRGB, + Tf::Astc5x4RgbaUnorm => ASTC_5x4_LDR, + Tf::Astc5x4RgbaUnormSrgb => ASTC_5x4_sRGB, + Tf::Astc5x5RgbaUnorm => ASTC_5x5_LDR, + Tf::Astc5x5RgbaUnormSrgb => ASTC_5x5_sRGB, + Tf::Astc6x5RgbaUnorm => ASTC_6x5_LDR, + Tf::Astc6x5RgbaUnormSrgb => ASTC_6x5_sRGB, + Tf::Astc6x6RgbaUnorm => ASTC_6x6_LDR, + Tf::Astc6x6RgbaUnormSrgb => ASTC_6x6_sRGB, + Tf::Astc8x5RgbaUnorm => ASTC_8x5_LDR, + Tf::Astc8x5RgbaUnormSrgb => ASTC_8x5_sRGB, + Tf::Astc8x6RgbaUnorm => ASTC_8x6_LDR, + Tf::Astc8x6RgbaUnormSrgb => ASTC_8x6_sRGB, + Tf::Astc10x5RgbaUnorm => ASTC_8x8_LDR, + Tf::Astc10x5RgbaUnormSrgb => ASTC_8x8_sRGB, + Tf::Astc10x6RgbaUnorm => ASTC_10x5_LDR, + Tf::Astc10x6RgbaUnormSrgb => ASTC_10x5_sRGB, + Tf::Astc8x8RgbaUnorm => ASTC_10x6_LDR, + Tf::Astc8x8RgbaUnormSrgb => ASTC_10x6_sRGB, + Tf::Astc10x8RgbaUnorm => ASTC_10x8_LDR, + Tf::Astc10x8RgbaUnormSrgb => ASTC_10x8_sRGB, + Tf::Astc10x10RgbaUnorm => ASTC_10x10_LDR, + Tf::Astc10x10RgbaUnormSrgb => ASTC_10x10_sRGB, + Tf::Astc12x10RgbaUnorm => ASTC_12x10_LDR, + Tf::Astc12x10RgbaUnormSrgb => ASTC_12x10_sRGB, + Tf::Astc12x12RgbaUnorm => ASTC_12x12_LDR, + Tf::Astc12x12RgbaUnormSrgb => ASTC_12x12_sRGB, + } + } +} + +impl super::PrivateDisabilities { + pub fn new(device: &mtl::Device) -> Self { + let is_intel = device.name().starts_with("Intel"); + Self { + broken_viewport_near_depth: is_intel + && !device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v4), + broken_layered_clear_image: is_intel, + } + } +} diff --git a/wgpu-hal/src/metal/command.rs b/wgpu-hal/src/metal/command.rs new file mode 100644 index 0000000000..902274fc16 --- /dev/null +++ b/wgpu-hal/src/metal/command.rs @@ -0,0 +1,867 @@ +use super::{conv, AsNative}; +use std::{mem, ops::Range}; + +const WORD_SIZE: usize = 4; + +impl Default for super::CommandState { + fn default() -> Self { + Self { + blit: None, + render: None, + compute: None, + raw_primitive_type: mtl::MTLPrimitiveType::Point, + index: None, + raw_wg_size: mtl::MTLSize::new(0, 0, 0), + stage_infos: Default::default(), + storage_buffer_length_map: Default::default(), + } + } +} + +impl super::CommandEncoder { + fn enter_blit(&mut self) -> &mtl::BlitCommandEncoderRef { + if self.state.blit.is_none() { + debug_assert!(self.state.render.is_none() && self.state.compute.is_none()); + let cmd_buf = self.raw_cmd_buf.as_ref().unwrap(); + self.state.blit = Some(cmd_buf.new_blit_command_encoder().to_owned()); + } + self.state.blit.as_ref().unwrap() + } + + pub(super) fn leave_blit(&mut self) { + if let Some(encoder) = self.state.blit.take() { + encoder.end_encoding(); + } + } + + fn enter_any(&mut self) -> &mtl::CommandEncoderRef { + if let Some(ref encoder) = self.state.render { + encoder + } else if let Some(ref encoder) = self.state.compute { + encoder + } else { + self.enter_blit() + } + } + + fn begin_pass(&mut self) { + self.state.storage_buffer_length_map.clear(); + self.state.stage_infos.vs.clear(); + self.state.stage_infos.fs.clear(); + self.state.stage_infos.cs.clear(); + self.leave_blit(); + } +} + +impl super::CommandState { + fn make_sizes_buffer_update<'a>( + &self, + stage: naga::ShaderStage, + result_sizes: &'a mut Vec, + ) -> Option<(u32, &'a [wgt::BufferSize])> { + let stage_info = &self.stage_infos[stage]; + let slot = stage_info.sizes_slot?; + result_sizes.clear(); + for br in stage_info.sized_bindings.iter() { + // If it's None, this isn't the right time to update the sizes + let size = self + .storage_buffer_length_map + .get(&(br.group, br.binding))?; + result_sizes.push(*size); + } + Some((slot as _, result_sizes)) + } +} + +impl crate::CommandEncoder for super::CommandEncoder { + unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> { + let queue = &self.raw_queue.lock(); + let retain_references = self.shared.settings.retain_command_buffer_references; + let raw = objc::rc::autoreleasepool(move || { + let cmd_buf_ref = if retain_references { + queue.new_command_buffer() + } else { + queue.new_command_buffer_with_unretained_references() + }; + cmd_buf_ref.to_owned() + }); + + if let Some(label) = label { + raw.set_label(label); + } + self.raw_cmd_buf = Some(raw); + + Ok(()) + } + unsafe fn discard_encoding(&mut self) { + self.leave_blit(); + self.raw_cmd_buf = None; + } + unsafe fn end_encoding(&mut self) -> Result { + self.leave_blit(); + Ok(super::CommandBuffer { + raw: self.raw_cmd_buf.take().unwrap(), + }) + } + unsafe fn reset_all(&mut self, _cmd_bufs: I) + where + I: Iterator, + { + //do nothing + } + + unsafe fn transition_buffers<'a, T>(&mut self, _barriers: T) + where + T: Iterator>, + { + } + + unsafe fn transition_textures<'a, T>(&mut self, _barriers: T) + where + T: Iterator>, + { + } + + unsafe fn fill_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange, value: u8) { + let encoder = self.enter_blit(); + encoder.fill_buffer(&buffer.raw, conv::map_range(&range), value); + } + + unsafe fn copy_buffer_to_buffer( + &mut self, + src: &super::Buffer, + dst: &super::Buffer, + regions: T, + ) where + T: Iterator, + { + let encoder = self.enter_blit(); + for copy in regions { + encoder.copy_from_buffer( + &src.raw, + copy.src_offset, + &dst.raw, + copy.dst_offset, + copy.size.get(), + ); + } + } + + unsafe fn copy_texture_to_texture( + &mut self, + src: &super::Texture, + _src_usage: crate::TextureUse, + dst: &super::Texture, + regions: T, + ) where + T: Iterator, + { + let encoder = self.enter_blit(); + for copy in regions { + let (src_slice, src_origin) = conv::map_origin(©.src_base.origin, src.raw_type); + let (dst_slice, dst_origin) = conv::map_origin(©.dst_base.origin, dst.raw_type); + let (slice_count, extent) = conv::map_extent(©.size, src.raw_type); + for slice in 0..slice_count { + encoder.copy_from_texture( + &src.raw, + src_slice + slice, + copy.src_base.mip_level as u64, + src_origin, + extent, + &dst.raw, + dst_slice + slice, + copy.dst_base.mip_level as u64, + dst_origin, + ); + } + } + } + + unsafe fn copy_buffer_to_texture( + &mut self, + src: &super::Buffer, + dst: &super::Texture, + regions: T, + ) where + T: Iterator, + { + let encoder = self.enter_blit(); + for copy in regions { + let (dst_slice, dst_origin) = conv::map_origin(©.texture_base.origin, dst.raw_type); + let (slice_count, extent) = conv::map_extent(©.size, dst.raw_type); + let bytes_per_row = copy + .buffer_layout + .bytes_per_row + .map_or(0, |v| v.get() as u64); + let bytes_per_image = copy + .buffer_layout + .rows_per_image + .map_or(0, |v| v.get() as u64 * bytes_per_row); + for slice in 0..slice_count { + let offset = copy.buffer_layout.offset + bytes_per_image * slice; + encoder.copy_from_buffer_to_texture( + &src.raw, + offset, + bytes_per_row, + bytes_per_image, + extent, + &dst.raw, + dst_slice + slice, + copy.texture_base.mip_level as u64, + dst_origin, + mtl::MTLBlitOption::empty(), + ); + } + } + } + + unsafe fn copy_texture_to_buffer( + &mut self, + src: &super::Texture, + _src_usage: crate::TextureUse, + dst: &super::Buffer, + regions: T, + ) where + T: Iterator, + { + let encoder = self.enter_blit(); + for copy in regions { + let (src_slice, src_origin) = conv::map_origin(©.texture_base.origin, src.raw_type); + let (slice_count, extent) = conv::map_extent(©.size, src.raw_type); + let bytes_per_row = copy + .buffer_layout + .bytes_per_row + .map_or(0, |v| v.get() as u64); + let bytes_per_image = copy + .buffer_layout + .rows_per_image + .map_or(0, |v| v.get() as u64 * bytes_per_row); + for slice in 0..slice_count { + let offset = copy.buffer_layout.offset + bytes_per_image * slice; + encoder.copy_from_texture_to_buffer( + &src.raw, + src_slice + slice, + copy.texture_base.mip_level as u64, + src_origin, + extent, + &dst.raw, + offset, + bytes_per_row, + bytes_per_image, + mtl::MTLBlitOption::empty(), + ); + } + } + } + + unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) { + match set.ty { + wgt::QueryType::Occlusion => { + self.state + .render + .as_ref() + .unwrap() + .set_visibility_result_mode( + mtl::MTLVisibilityResultMode::Boolean, + index as u64 * crate::QUERY_SIZE, + ); + } + _ => {} + } + } + unsafe fn end_query(&mut self, set: &super::QuerySet, _index: u32) { + match set.ty { + wgt::QueryType::Occlusion => { + self.state + .render + .as_ref() + .unwrap() + .set_visibility_result_mode(mtl::MTLVisibilityResultMode::Disabled, 0); + } + _ => {} + } + } + unsafe fn write_timestamp(&mut self, _set: &super::QuerySet, _index: u32) {} + unsafe fn reset_queries(&mut self, set: &super::QuerySet, range: Range) { + let encoder = self.enter_blit(); + let raw_range = mtl::NSRange { + location: range.start as u64 * crate::QUERY_SIZE, + length: (range.end - range.start) as u64 * crate::QUERY_SIZE, + }; + encoder.fill_buffer(&set.raw_buffer, raw_range, 0); + } + unsafe fn copy_query_results( + &mut self, + set: &super::QuerySet, + range: Range, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + ) { + let encoder = self.enter_blit(); + let size = (range.end - range.start) as u64 * crate::QUERY_SIZE; + encoder.copy_from_buffer( + &set.raw_buffer, + range.start as u64 * crate::QUERY_SIZE, + &buffer.raw, + offset, + size, + ); + } + + // render + + unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor) { + self.begin_pass(); + self.state.index = None; + + let descriptor = mtl::RenderPassDescriptor::new(); + //TODO: set visibility results buffer + + for (i, at) in desc.color_attachments.iter().enumerate() { + let at_descriptor = descriptor.color_attachments().object_at(i as u64).unwrap(); + at_descriptor.set_texture(Some(&at.target.view.raw)); + if let Some(ref resolve) = at.resolve_target { + //Note: the selection of levels and slices is already handled by `TextureView` + at_descriptor.set_resolve_texture(Some(&resolve.view.raw)); + } + let load_action = if at.ops.contains(crate::AttachmentOp::LOAD) { + mtl::MTLLoadAction::Load + } else { + at_descriptor.set_clear_color(conv::map_clear_color(&at.clear_value)); + mtl::MTLLoadAction::Clear + }; + let store_action = conv::map_store_action( + at.ops.contains(crate::AttachmentOp::STORE), + at.resolve_target.is_some(), + ); + at_descriptor.set_load_action(load_action); + at_descriptor.set_store_action(store_action); + } + + if let Some(ref at) = desc.depth_stencil_attachment { + if at.target.view.aspects.contains(crate::FormatAspect::DEPTH) { + let at_descriptor = descriptor.depth_attachment().unwrap(); + at_descriptor.set_texture(Some(&at.target.view.raw)); + + let load_action = if at.depth_ops.contains(crate::AttachmentOp::LOAD) { + mtl::MTLLoadAction::Load + } else { + at_descriptor.set_clear_depth(at.clear_value.0 as f64); + mtl::MTLLoadAction::Clear + }; + let store_action = if at.depth_ops.contains(crate::AttachmentOp::STORE) { + mtl::MTLStoreAction::Store + } else { + mtl::MTLStoreAction::DontCare + }; + at_descriptor.set_load_action(load_action); + at_descriptor.set_store_action(store_action); + } + if at + .target + .view + .aspects + .contains(crate::FormatAspect::STENCIL) + { + let at_descriptor = descriptor.stencil_attachment().unwrap(); + at_descriptor.set_texture(Some(&at.target.view.raw)); + + let load_action = if at.depth_ops.contains(crate::AttachmentOp::LOAD) { + mtl::MTLLoadAction::Load + } else { + at_descriptor.set_clear_stencil(at.clear_value.1); + mtl::MTLLoadAction::Clear + }; + let store_action = if at.depth_ops.contains(crate::AttachmentOp::STORE) { + mtl::MTLStoreAction::Store + } else { + mtl::MTLStoreAction::DontCare + }; + at_descriptor.set_load_action(load_action); + at_descriptor.set_store_action(store_action); + } + } + + let raw = self.raw_cmd_buf.as_ref().unwrap(); + let encoder = raw.new_render_command_encoder(descriptor); + if let Some(label) = desc.label { + encoder.set_label(label); + } + self.state.render = Some(encoder.to_owned()); + } + + unsafe fn end_render_pass(&mut self) { + self.state.render.take().unwrap().end_encoding(); + } + + unsafe fn set_bind_group( + &mut self, + layout: &super::PipelineLayout, + group_index: u32, + group: &super::BindGroup, + dynamic_offsets: &[wgt::DynamicOffset], + ) { + let bg_info = &layout.bind_group_infos[group_index as usize]; + + if let Some(ref encoder) = self.state.render { + let mut changes_sizes_buffer = false; + for index in 0..group.counters.vs.buffers { + let buf = &group.buffers[index as usize]; + let mut offset = buf.offset; + if let Some(dyn_index) = buf.dynamic_index { + offset += dynamic_offsets[dyn_index as usize] as wgt::BufferAddress; + } + encoder.set_vertex_buffer( + (bg_info.base_resource_indices.vs.buffers + index) as u64, + Some(buf.ptr.as_native()), + offset, + ); + if let Some(size) = buf.binding_size { + self.state + .storage_buffer_length_map + .insert((group_index, buf.binding_location), size); + changes_sizes_buffer = true; + } + } + if changes_sizes_buffer { + if let Some((index, sizes)) = self.state.make_sizes_buffer_update( + naga::ShaderStage::Vertex, + &mut self.temp.binding_sizes, + ) { + encoder.set_vertex_bytes( + index as _, + (sizes.len() * WORD_SIZE) as u64, + sizes.as_ptr() as _, + ); + } + } + + changes_sizes_buffer = false; + for index in 0..group.counters.fs.buffers { + let buf = &group.buffers[(group.counters.vs.buffers + index) as usize]; + let mut offset = buf.offset; + if let Some(dyn_index) = buf.dynamic_index { + offset += dynamic_offsets[dyn_index as usize] as wgt::BufferAddress; + } + encoder.set_fragment_buffer( + (bg_info.base_resource_indices.fs.buffers + index) as u64, + Some(buf.ptr.as_native()), + offset, + ); + if let Some(size) = buf.binding_size { + self.state + .storage_buffer_length_map + .insert((group_index, buf.binding_location), size); + changes_sizes_buffer = true; + } + } + if changes_sizes_buffer { + if let Some((index, sizes)) = self.state.make_sizes_buffer_update( + naga::ShaderStage::Fragment, + &mut self.temp.binding_sizes, + ) { + encoder.set_fragment_bytes( + index as _, + (sizes.len() * WORD_SIZE) as u64, + sizes.as_ptr() as _, + ); + } + } + + for index in 0..group.counters.vs.samplers { + let res = group.samplers[index as usize]; + encoder.set_vertex_sampler_state( + (bg_info.base_resource_indices.vs.samplers + index) as u64, + Some(res.as_native()), + ); + } + for index in 0..group.counters.fs.samplers { + let res = group.samplers[(group.counters.vs.samplers + index) as usize]; + encoder.set_fragment_sampler_state( + (bg_info.base_resource_indices.fs.samplers + index) as u64, + Some(res.as_native()), + ); + } + + for index in 0..group.counters.vs.textures { + let res = group.textures[index as usize]; + encoder.set_vertex_texture( + (bg_info.base_resource_indices.vs.textures + index) as u64, + Some(res.as_native()), + ); + } + for index in 0..group.counters.fs.textures { + let res = group.textures[(group.counters.vs.textures + index) as usize]; + encoder.set_fragment_texture( + (bg_info.base_resource_indices.fs.textures + index) as u64, + Some(res.as_native()), + ); + } + } + + if let Some(ref encoder) = self.state.compute { + let index_base = super::ResourceData { + buffers: group.counters.vs.buffers + group.counters.fs.buffers, + samplers: group.counters.vs.samplers + group.counters.fs.samplers, + textures: group.counters.vs.textures + group.counters.fs.textures, + }; + + let mut changes_sizes_buffer = false; + for index in 0..group.counters.cs.buffers { + let buf = &group.buffers[(index_base.buffers + index) as usize]; + let mut offset = buf.offset; + if let Some(dyn_index) = buf.dynamic_index { + offset += dynamic_offsets[dyn_index as usize] as wgt::BufferAddress; + } + encoder.set_buffer( + (bg_info.base_resource_indices.cs.buffers + index) as u64, + Some(buf.ptr.as_native()), + offset, + ); + if let Some(size) = buf.binding_size { + self.state + .storage_buffer_length_map + .insert((group_index, buf.binding_location), size); + changes_sizes_buffer = true; + } + } + if changes_sizes_buffer { + if let Some((index, sizes)) = self.state.make_sizes_buffer_update( + naga::ShaderStage::Compute, + &mut self.temp.binding_sizes, + ) { + encoder.set_bytes( + index as _, + (sizes.len() * WORD_SIZE) as u64, + sizes.as_ptr() as _, + ); + } + } + + for index in 0..group.counters.cs.samplers { + let res = group.samplers[(index_base.samplers + index) as usize]; + encoder.set_sampler_state( + (bg_info.base_resource_indices.cs.samplers + index) as u64, + Some(res.as_native()), + ); + } + for index in 0..group.counters.cs.textures { + let res = group.textures[(index_base.textures + index) as usize]; + encoder.set_texture( + (bg_info.base_resource_indices.cs.textures + index) as u64, + Some(res.as_native()), + ); + } + } + } + + unsafe fn set_push_constants( + &mut self, + _layout: &super::PipelineLayout, + _stages: wgt::ShaderStage, + _offset: u32, + _data: &[u32], + ) { + //TODO + } + + unsafe fn insert_debug_marker(&mut self, label: &str) { + self.enter_any().insert_debug_signpost(label); + } + unsafe fn begin_debug_marker(&mut self, group_label: &str) { + self.enter_any().push_debug_group(group_label); + } + unsafe fn end_debug_marker(&mut self) { + self.enter_any().pop_debug_group(); + } + + unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) { + self.state.raw_primitive_type = pipeline.raw_primitive_type; + self.state.stage_infos.vs.assign_from(&pipeline.vs_info); + self.state.stage_infos.fs.assign_from(&pipeline.fs_info); + + let encoder = self.state.render.as_ref().unwrap(); + encoder.set_render_pipeline_state(&pipeline.raw); + encoder.set_front_facing_winding(pipeline.raw_front_winding); + encoder.set_cull_mode(pipeline.raw_cull_mode); + if let Some(depth_clip) = pipeline.raw_depth_clip_mode { + encoder.set_depth_clip_mode(depth_clip); + } + if let Some((ref state, bias)) = pipeline.depth_stencil { + encoder.set_depth_stencil_state(state); + encoder.set_depth_bias(bias.constant as f32, bias.slope_scale, bias.clamp); + } + + { + if let Some((index, sizes)) = self + .state + .make_sizes_buffer_update(naga::ShaderStage::Vertex, &mut self.temp.binding_sizes) + { + encoder.set_vertex_bytes( + index as _, + (sizes.len() * WORD_SIZE) as u64, + sizes.as_ptr() as _, + ); + } + } + if pipeline.fs_lib.is_some() { + if let Some((index, sizes)) = self + .state + .make_sizes_buffer_update(naga::ShaderStage::Fragment, &mut self.temp.binding_sizes) + { + encoder.set_fragment_bytes( + index as _, + (sizes.len() * WORD_SIZE) as u64, + sizes.as_ptr() as _, + ); + } + } + } + + unsafe fn set_index_buffer<'a>( + &mut self, + binding: crate::BufferBinding<'a, super::Api>, + format: wgt::IndexFormat, + ) { + let (stride, raw_type) = match format { + wgt::IndexFormat::Uint16 => (2, mtl::MTLIndexType::UInt16), + wgt::IndexFormat::Uint32 => (4, mtl::MTLIndexType::UInt32), + }; + self.state.index = Some(super::IndexState { + buffer_ptr: AsNative::from(binding.buffer.raw.as_ref()), + offset: binding.offset, + stride, + raw_type, + }); + } + + unsafe fn set_vertex_buffer<'a>( + &mut self, + index: u32, + binding: crate::BufferBinding<'a, super::Api>, + ) { + let buffer_index = self.shared.private_caps.max_buffers_per_stage as u64 - 1 - index as u64; + let encoder = self.state.render.as_ref().unwrap(); + encoder.set_vertex_buffer(buffer_index, Some(&binding.buffer.raw), binding.offset); + } + + unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth_range: Range) { + let zfar = if self.shared.disabilities.broken_viewport_near_depth { + depth_range.end - depth_range.start + } else { + depth_range.end + }; + let encoder = self.state.render.as_ref().unwrap(); + encoder.set_viewport(mtl::MTLViewport { + originX: rect.x as _, + originY: rect.y as _, + width: rect.w as _, + height: rect.h as _, + znear: depth_range.start as _, + zfar: zfar as _, + }); + } + unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) { + //TODO: support empty scissors by modifying the viewport + let scissor = mtl::MTLScissorRect { + x: rect.x as _, + y: rect.y as _, + width: rect.w as _, + height: rect.h as _, + }; + let encoder = self.state.render.as_ref().unwrap(); + encoder.set_scissor_rect(scissor); + } + unsafe fn set_stencil_reference(&mut self, value: u32) { + let encoder = self.state.render.as_ref().unwrap(); + encoder.set_stencil_front_back_reference_value(value, value); + } + unsafe fn set_blend_constants(&mut self, color: &wgt::Color) { + let encoder = self.state.render.as_ref().unwrap(); + encoder.set_blend_color( + color.r as f32, + color.g as f32, + color.b as f32, + color.a as f32, + ); + } + + unsafe fn draw( + &mut self, + start_vertex: u32, + vertex_count: u32, + start_instance: u32, + instance_count: u32, + ) { + let encoder = self.state.render.as_ref().unwrap(); + if start_instance != 0 { + encoder.draw_primitives_instanced_base_instance( + self.state.raw_primitive_type, + start_vertex as _, + vertex_count as _, + instance_count as _, + start_instance as _, + ); + } else if instance_count != 1 { + encoder.draw_primitives_instanced( + self.state.raw_primitive_type, + start_vertex as _, + vertex_count as _, + instance_count as _, + ); + } else { + encoder.draw_primitives( + self.state.raw_primitive_type, + start_vertex as _, + vertex_count as _, + ); + } + } + + unsafe fn draw_indexed( + &mut self, + start_index: u32, + index_count: u32, + base_vertex: i32, + start_instance: u32, + instance_count: u32, + ) { + let encoder = self.state.render.as_ref().unwrap(); + let index = self.state.index.as_ref().unwrap(); + let offset = index.offset + index.stride * start_index as wgt::BufferAddress; + if base_vertex != 0 || start_instance != 0 { + encoder.draw_indexed_primitives_instanced_base_instance( + self.state.raw_primitive_type, + index_count as _, + index.raw_type, + index.buffer_ptr.as_native(), + offset, + instance_count as _, + base_vertex as _, + start_instance as _, + ); + } else if instance_count != 1 { + encoder.draw_indexed_primitives_instanced( + self.state.raw_primitive_type, + index_count as _, + index.raw_type, + index.buffer_ptr.as_native(), + offset, + instance_count as _, + ); + } else { + encoder.draw_indexed_primitives( + self.state.raw_primitive_type, + index_count as _, + index.raw_type, + index.buffer_ptr.as_native(), + offset, + ); + } + } + + unsafe fn draw_indirect( + &mut self, + buffer: &super::Buffer, + mut offset: wgt::BufferAddress, + draw_count: u32, + ) { + let encoder = self.state.render.as_ref().unwrap(); + for _ in 0..draw_count { + encoder.draw_primitives_indirect(self.state.raw_primitive_type, &buffer.raw, offset); + offset += mem::size_of::() as wgt::BufferAddress; + } + } + + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &super::Buffer, + mut offset: wgt::BufferAddress, + draw_count: u32, + ) { + let encoder = self.state.render.as_ref().unwrap(); + let index = self.state.index.as_ref().unwrap(); + for _ in 0..draw_count { + encoder.draw_indexed_primitives_indirect( + self.state.raw_primitive_type, + index.raw_type, + index.buffer_ptr.as_native(), + index.offset, + &buffer.raw, + offset, + ); + offset += mem::size_of::() as wgt::BufferAddress; + } + } + + unsafe fn draw_indirect_count( + &mut self, + _buffer: &super::Buffer, + _offset: wgt::BufferAddress, + _count_buffer: &super::Buffer, + _count_offset: wgt::BufferAddress, + _max_count: u32, + ) { + //TODO + } + unsafe fn draw_indexed_indirect_count( + &mut self, + _buffer: &super::Buffer, + _offset: wgt::BufferAddress, + _count_buffer: &super::Buffer, + _count_offset: wgt::BufferAddress, + _max_count: u32, + ) { + //TODO + } + + // compute + + unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor) { + self.begin_pass(); + + let raw = self.raw_cmd_buf.as_ref().unwrap(); + let encoder = raw.new_compute_command_encoder(); + if let Some(label) = desc.label { + encoder.set_label(label); + } + self.state.compute = Some(encoder.to_owned()); + } + unsafe fn end_compute_pass(&mut self) { + self.state.compute.take().unwrap().end_encoding(); + } + + unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) { + self.state.raw_wg_size = pipeline.work_group_size; + self.state.stage_infos.cs.assign_from(&pipeline.cs_info); + + let encoder = self.state.compute.as_ref().unwrap(); + encoder.set_compute_pipeline_state(&pipeline.raw); + + if let Some((index, sizes)) = self + .state + .make_sizes_buffer_update(naga::ShaderStage::Compute, &mut self.temp.binding_sizes) + { + encoder.set_bytes( + index as _, + (sizes.len() * WORD_SIZE) as u64, + sizes.as_ptr() as _, + ); + } + } + + unsafe fn dispatch(&mut self, count: [u32; 3]) { + let encoder = self.state.compute.as_ref().unwrap(); + let raw_count = mtl::MTLSize { + width: count[0] as u64, + height: count[1] as u64, + depth: count[2] as u64, + }; + encoder.dispatch_thread_groups(raw_count, self.state.raw_wg_size); + } + + unsafe fn dispatch_indirect(&mut self, buffer: &super::Buffer, offset: wgt::BufferAddress) { + let encoder = self.state.compute.as_ref().unwrap(); + encoder.dispatch_thread_groups_indirect(&buffer.raw, offset, self.state.raw_wg_size); + } +} diff --git a/wgpu-hal/src/metal/conv.rs b/wgpu-hal/src/metal/conv.rs new file mode 100644 index 0000000000..9fc7727b26 --- /dev/null +++ b/wgpu-hal/src/metal/conv.rs @@ -0,0 +1,311 @@ +pub fn map_texture_usage(usage: crate::TextureUse) -> mtl::MTLTextureUsage { + use crate::TextureUse as Tu; + + let mut mtl_usage = mtl::MTLTextureUsage::Unknown; + + mtl_usage.set( + mtl::MTLTextureUsage::RenderTarget, + usage.intersects(Tu::COLOR_TARGET | Tu::DEPTH_STENCIL_READ | Tu::DEPTH_STENCIL_WRITE), + ); + mtl_usage.set( + mtl::MTLTextureUsage::ShaderRead, + usage.intersects(Tu::SAMPLED | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_LOAD), + ); + mtl_usage.set( + mtl::MTLTextureUsage::ShaderWrite, + usage.intersects(Tu::STORAGE_STORE), + ); + + mtl_usage +} + +pub fn map_texture_view_dimension(dim: wgt::TextureViewDimension) -> mtl::MTLTextureType { + use mtl::MTLTextureType::*; + use wgt::TextureViewDimension as Tvd; + match dim { + Tvd::D1 => D1, + Tvd::D2 => D2, + Tvd::D2Array => D2Array, + Tvd::D3 => D3, + Tvd::Cube => Cube, + Tvd::CubeArray => CubeArray, + } +} + +pub fn map_compare_function(fun: wgt::CompareFunction) -> mtl::MTLCompareFunction { + use mtl::MTLCompareFunction::*; + use wgt::CompareFunction as Cf; + match fun { + Cf::Never => Never, + Cf::Less => Less, + Cf::LessEqual => LessEqual, + Cf::Equal => Equal, + Cf::GreaterEqual => GreaterEqual, + Cf::Greater => Greater, + Cf::NotEqual => NotEqual, + Cf::Always => Always, + } +} + +pub fn map_filter_mode(filter: wgt::FilterMode) -> mtl::MTLSamplerMinMagFilter { + use mtl::MTLSamplerMinMagFilter::*; + match filter { + wgt::FilterMode::Nearest => Nearest, + wgt::FilterMode::Linear => Linear, + } +} + +pub fn map_address_mode(address: wgt::AddressMode) -> mtl::MTLSamplerAddressMode { + use mtl::MTLSamplerAddressMode::*; + use wgt::AddressMode as Fm; + match address { + Fm::Repeat => Repeat, + Fm::MirrorRepeat => MirrorRepeat, + Fm::ClampToEdge => ClampToEdge, + Fm::ClampToBorder => ClampToBorderColor, + //Fm::MirrorClamp => MirrorClampToEdge, + } +} + +pub fn map_border_color(border_color: wgt::SamplerBorderColor) -> mtl::MTLSamplerBorderColor { + use mtl::MTLSamplerBorderColor::*; + match border_color { + wgt::SamplerBorderColor::TransparentBlack => TransparentBlack, + wgt::SamplerBorderColor::OpaqueBlack => OpaqueBlack, + wgt::SamplerBorderColor::OpaqueWhite => OpaqueWhite, + } +} + +pub fn map_primitive_topology( + topology: wgt::PrimitiveTopology, +) -> (mtl::MTLPrimitiveTopologyClass, mtl::MTLPrimitiveType) { + use wgt::PrimitiveTopology as Pt; + match topology { + Pt::PointList => ( + mtl::MTLPrimitiveTopologyClass::Point, + mtl::MTLPrimitiveType::Point, + ), + Pt::LineList => ( + mtl::MTLPrimitiveTopologyClass::Line, + mtl::MTLPrimitiveType::Line, + ), + Pt::LineStrip => ( + mtl::MTLPrimitiveTopologyClass::Line, + mtl::MTLPrimitiveType::LineStrip, + ), + Pt::TriangleList => ( + mtl::MTLPrimitiveTopologyClass::Triangle, + mtl::MTLPrimitiveType::Triangle, + ), + Pt::TriangleStrip => ( + mtl::MTLPrimitiveTopologyClass::Triangle, + mtl::MTLPrimitiveType::TriangleStrip, + ), + } +} + +pub fn map_color_write(mask: wgt::ColorWrite) -> mtl::MTLColorWriteMask { + let mut raw_mask = mtl::MTLColorWriteMask::empty(); + + if mask.contains(wgt::ColorWrite::RED) { + raw_mask |= mtl::MTLColorWriteMask::Red; + } + if mask.contains(wgt::ColorWrite::GREEN) { + raw_mask |= mtl::MTLColorWriteMask::Green; + } + if mask.contains(wgt::ColorWrite::BLUE) { + raw_mask |= mtl::MTLColorWriteMask::Blue; + } + if mask.contains(wgt::ColorWrite::ALPHA) { + raw_mask |= mtl::MTLColorWriteMask::Alpha; + } + + raw_mask +} + +pub fn map_blend_factor(factor: wgt::BlendFactor) -> mtl::MTLBlendFactor { + use mtl::MTLBlendFactor::*; + use wgt::BlendFactor as Bf; + + match factor { + Bf::Zero => Zero, + Bf::One => One, + Bf::Src => SourceColor, + Bf::OneMinusSrc => OneMinusSourceColor, + Bf::Dst => DestinationColor, + Bf::OneMinusDst => OneMinusDestinationColor, + Bf::SrcAlpha => SourceAlpha, + Bf::OneMinusSrcAlpha => OneMinusSourceAlpha, + Bf::DstAlpha => DestinationAlpha, + Bf::OneMinusDstAlpha => OneMinusDestinationAlpha, + Bf::Constant => BlendColor, + Bf::OneMinusConstant => OneMinusBlendColor, + //Bf::ConstantAlpha => BlendAlpha, + //Bf::OneMinusConstantAlpha => OneMinusBlendAlpha, + Bf::SrcAlphaSaturated => SourceAlphaSaturated, + //Bf::Src1 => Source1Color, + //Bf::OneMinusSrc1 => OneMinusSource1Color, + //Bf::Src1Alpha => Source1Alpha, + //Bf::OneMinusSrc1Alpha => OneMinusSource1Alpha, + } +} + +pub fn map_blend_op(operation: wgt::BlendOperation) -> mtl::MTLBlendOperation { + use mtl::MTLBlendOperation::*; + use wgt::BlendOperation as Bo; + + match operation { + Bo::Add => Add, + Bo::Subtract => Subtract, + Bo::ReverseSubtract => ReverseSubtract, + Bo::Min => Min, + Bo::Max => Max, + } +} + +pub fn map_blend_component( + component: &wgt::BlendComponent, +) -> ( + mtl::MTLBlendOperation, + mtl::MTLBlendFactor, + mtl::MTLBlendFactor, +) { + ( + map_blend_op(component.operation), + map_blend_factor(component.src_factor), + map_blend_factor(component.dst_factor), + ) +} + +pub fn map_vertex_format(format: wgt::VertexFormat) -> mtl::MTLVertexFormat { + use mtl::MTLVertexFormat::*; + use wgt::VertexFormat as Vf; + + match format { + Vf::Unorm8x2 => UChar2Normalized, + Vf::Snorm8x2 => Char2Normalized, + Vf::Uint8x2 => UChar2, + Vf::Sint8x2 => Char2, + Vf::Unorm8x4 => UChar4Normalized, + Vf::Snorm8x4 => Char4Normalized, + Vf::Uint8x4 => UChar4, + Vf::Sint8x4 => Char4, + Vf::Unorm16x2 => UShort2Normalized, + Vf::Snorm16x2 => Short2Normalized, + Vf::Uint16x2 => UShort2, + Vf::Sint16x2 => Short2, + Vf::Float16x2 => Half2, + Vf::Unorm16x4 => UShort4Normalized, + Vf::Snorm16x4 => Short4Normalized, + Vf::Uint16x4 => UShort4, + Vf::Sint16x4 => Short4, + Vf::Float16x4 => Half4, + Vf::Uint32 => UInt, + Vf::Sint32 => Int, + Vf::Float32 => Float, + Vf::Uint32x2 => UInt2, + Vf::Sint32x2 => Int2, + Vf::Float32x2 => Float2, + Vf::Uint32x3 => UInt3, + Vf::Sint32x3 => Int3, + Vf::Float32x3 => Float3, + Vf::Uint32x4 => UInt4, + Vf::Sint32x4 => Int4, + Vf::Float32x4 => Float4, + Vf::Float64 | Vf::Float64x2 | Vf::Float64x3 | Vf::Float64x4 => unimplemented!(), + } +} + +pub fn map_step_mode(mode: wgt::InputStepMode) -> mtl::MTLVertexStepFunction { + match mode { + wgt::InputStepMode::Vertex => mtl::MTLVertexStepFunction::PerVertex, + wgt::InputStepMode::Instance => mtl::MTLVertexStepFunction::PerInstance, + } +} + +pub fn map_stencil_op(op: wgt::StencilOperation) -> mtl::MTLStencilOperation { + use mtl::MTLStencilOperation::*; + use wgt::StencilOperation as So; + + match op { + So::Keep => Keep, + So::Zero => Zero, + So::Replace => Replace, + So::IncrementClamp => IncrementClamp, + So::IncrementWrap => IncrementWrap, + So::DecrementClamp => DecrementClamp, + So::DecrementWrap => DecrementWrap, + So::Invert => Invert, + } +} + +pub fn map_winding(winding: wgt::FrontFace) -> mtl::MTLWinding { + match winding { + wgt::FrontFace::Cw => mtl::MTLWinding::Clockwise, + wgt::FrontFace::Ccw => mtl::MTLWinding::CounterClockwise, + } +} + +pub fn map_cull_mode(face: Option) -> mtl::MTLCullMode { + match face { + None => mtl::MTLCullMode::None, + Some(wgt::Face::Front) => mtl::MTLCullMode::Front, + Some(wgt::Face::Back) => mtl::MTLCullMode::Back, + } +} + +pub fn map_range(range: &crate::MemoryRange) -> mtl::NSRange { + mtl::NSRange { + location: range.start, + length: range.end - range.start, + } +} + +pub fn map_extent(extent: &wgt::Extent3d, raw_type: mtl::MTLTextureType) -> (u64, mtl::MTLSize) { + let (depth, array_layers) = match raw_type { + mtl::MTLTextureType::D3 => (extent.depth_or_array_layers as u64, 1), + _ => (1, extent.depth_or_array_layers as u64), + }; + ( + array_layers, + mtl::MTLSize { + width: extent.width as u64, + height: extent.height as u64, + depth, + }, + ) +} + +pub fn map_origin(origin: &wgt::Origin3d, raw_type: mtl::MTLTextureType) -> (u64, mtl::MTLOrigin) { + let (z, slice) = match raw_type { + mtl::MTLTextureType::D3 => (origin.z as u64, 0), + _ => (0, origin.z as u64), + }; + ( + slice, + mtl::MTLOrigin { + x: origin.x as u64, + y: origin.y as u64, + z, + }, + ) +} + +pub fn map_store_action(store: bool, resolve: bool) -> mtl::MTLStoreAction { + use mtl::MTLStoreAction::*; + match (store, resolve) { + (true, true) => StoreAndMultisampleResolve, + (false, true) => MultisampleResolve, + (true, false) => Store, + (false, false) => DontCare, + } +} + +pub fn map_clear_color(color: &wgt::Color) -> mtl::MTLClearColor { + mtl::MTLClearColor { + red: color.r, + green: color.g, + blue: color.b, + alpha: color.a, + } +} diff --git a/wgpu-hal/src/metal/device.rs b/wgpu-hal/src/metal/device.rs new file mode 100644 index 0000000000..3a6f80d669 --- /dev/null +++ b/wgpu-hal/src/metal/device.rs @@ -0,0 +1,947 @@ +use std::{ + ptr, + sync::{atomic, Arc}, + thread, time, +}; + +use super::conv; +use crate::util::map_naga_stage; + +type DeviceResult = Result; + +struct CompiledShader { + library: mtl::Library, + function: mtl::Function, + wg_size: mtl::MTLSize, + sized_bindings: Vec, +} + +fn create_stencil_desc( + face: &wgt::StencilFaceState, + read_mask: u32, + write_mask: u32, +) -> mtl::StencilDescriptor { + let desc = mtl::StencilDescriptor::new(); + desc.set_stencil_compare_function(conv::map_compare_function(face.compare)); + desc.set_read_mask(read_mask); + desc.set_write_mask(write_mask); + desc.set_stencil_failure_operation(conv::map_stencil_op(face.fail_op)); + desc.set_depth_failure_operation(conv::map_stencil_op(face.depth_fail_op)); + desc.set_depth_stencil_pass_operation(conv::map_stencil_op(face.pass_op)); + desc +} + +fn create_depth_stencil_desc(state: &wgt::DepthStencilState) -> mtl::DepthStencilDescriptor { + let desc = mtl::DepthStencilDescriptor::new(); + desc.set_depth_compare_function(conv::map_compare_function(state.depth_compare)); + desc.set_depth_write_enabled(state.depth_write_enabled); + let s = &state.stencil; + if s.is_enabled() { + let front_desc = create_stencil_desc(&s.front, s.read_mask, s.write_mask); + desc.set_front_face_stencil(Some(&front_desc)); + let back_desc = create_stencil_desc(&s.back, s.read_mask, s.write_mask); + desc.set_front_face_stencil(Some(&back_desc)); + } + desc +} + +impl super::Device { + fn load_shader( + &self, + stage: &crate::ProgrammableStage, + layout: &super::PipelineLayout, + primitive_class: mtl::MTLPrimitiveTopologyClass, + naga_stage: naga::ShaderStage, + ) -> Result { + let stage_bit = map_naga_stage(naga_stage); + let pipeline_options = naga::back::msl::PipelineOptions { + allow_point_size: match primitive_class { + mtl::MTLPrimitiveTopologyClass::Point => true, + _ => false, + }, + }; + + let module = &stage.module.raw.module; + let (source, info) = naga::back::msl::write_string( + module, + &stage.module.raw.info, + &layout.naga_options, + &pipeline_options, + ) + .map_err(|e| crate::PipelineError::Linkage(stage_bit, format!("MSL: {:?}", e)))?; + + let options = mtl::CompileOptions::new(); + options.set_language_version(self.shared.private_caps.msl_version); + + let library = self + .shared + .device + .lock() + .new_library_with_source(source.as_ref(), &options) + .map_err(|err| { + log::warn!("Naga generated shader:\n{}", source); + crate::PipelineError::Linkage(stage_bit, format!("Metal: {}", err)) + })?; + + // collect sizes indices + let mut sized_bindings = Vec::new(); + for (_handle, var) in module.global_variables.iter() { + if let naga::TypeInner::Struct { ref members, .. } = module.types[var.ty].inner { + if let Some(member) = members.last() { + if let naga::TypeInner::Array { + size: naga::ArraySize::Dynamic, + .. + } = module.types[member.ty].inner + { + // Note: unwraps are fine, since the MSL is already generated + let br = var.binding.clone().unwrap(); + sized_bindings.push(br); + } + } + } + } + + let (ep, internal_name) = module + .entry_points + .iter() + .zip(info.entry_point_names) + .find(|&(ep, _)| ep.stage == naga_stage && ep.name == stage.entry_point) + .ok_or(crate::PipelineError::EntryPoint(naga_stage))?; + + let name = internal_name + .as_ref() + .map_err(|e| crate::PipelineError::Linkage(stage_bit, format!("{}", e)))?; + let wg_size = mtl::MTLSize { + width: ep.workgroup_size[0] as _, + height: ep.workgroup_size[1] as _, + depth: ep.workgroup_size[2] as _, + }; + + let function = library.get_function(name, None).map_err(|e| { + log::error!("get_function: {:?}", e); + crate::PipelineError::EntryPoint(naga_stage) + })?; + + Ok(CompiledShader { + library, + function, + wg_size, + sized_bindings, + }) + } +} + +impl crate::Device for super::Device { + unsafe fn exit(self) {} + + unsafe fn create_buffer(&self, desc: &crate::BufferDescriptor) -> DeviceResult { + let map_read = desc.usage.contains(crate::BufferUse::MAP_READ); + let map_write = desc.usage.contains(crate::BufferUse::MAP_WRITE); + + let mut options = mtl::MTLResourceOptions::empty(); + options |= if map_read || map_write { + mtl::MTLResourceOptions::StorageModeShared + } else { + mtl::MTLResourceOptions::StorageModePrivate + }; + options.set( + mtl::MTLResourceOptions::CPUCacheModeWriteCombined, + map_write, + ); + + //TODO: HazardTrackingModeUntracked + + let raw = self.shared.device.lock().new_buffer(desc.size, options); + if let Some(label) = desc.label { + raw.set_label(label); + } + Ok(super::Buffer { + raw, + size: desc.size, + options, + }) + } + unsafe fn destroy_buffer(&self, _buffer: super::Buffer) {} + + unsafe fn map_buffer( + &self, + buffer: &super::Buffer, + range: crate::MemoryRange, + ) -> DeviceResult { + let ptr = buffer.raw.contents() as *mut u8; + assert!(!ptr.is_null()); + Ok(crate::BufferMapping { + ptr: ptr::NonNull::new(ptr.offset(range.start as isize)).unwrap(), + is_coherent: true, + }) + } + + unsafe fn unmap_buffer(&self, _buffer: &super::Buffer) -> DeviceResult<()> { + Ok(()) + } + unsafe fn flush_mapped_ranges(&self, _buffer: &super::Buffer, _ranges: I) {} + unsafe fn invalidate_mapped_ranges(&self, _buffer: &super::Buffer, _ranges: I) {} + + unsafe fn create_texture( + &self, + desc: &crate::TextureDescriptor, + ) -> DeviceResult { + let mtl_format = self.shared.private_caps.map_format(desc.format); + + let descriptor = mtl::TextureDescriptor::new(); + let mut array_layers = desc.size.depth_or_array_layers; + let mtl_type = match desc.dimension { + wgt::TextureDimension::D1 => { + if desc.size.depth_or_array_layers > 1 { + descriptor.set_array_length(desc.size.depth_or_array_layers as u64); + mtl::MTLTextureType::D1Array + } else { + mtl::MTLTextureType::D1 + } + } + wgt::TextureDimension::D2 => { + if desc.sample_count > 1 { + descriptor.set_sample_count(desc.sample_count as u64); + mtl::MTLTextureType::D2Multisample + } else if desc.size.depth_or_array_layers > 1 { + descriptor.set_array_length(desc.size.depth_or_array_layers as u64); + mtl::MTLTextureType::D2Array + } else { + mtl::MTLTextureType::D2 + } + } + wgt::TextureDimension::D3 => { + descriptor.set_depth(desc.size.depth_or_array_layers as u64); + array_layers = 1; + mtl::MTLTextureType::D3 + } + }; + + descriptor.set_texture_type(mtl_type); + descriptor.set_width(desc.size.width as u64); + descriptor.set_height(desc.size.height as u64); + descriptor.set_mipmap_level_count(desc.mip_level_count as u64); + descriptor.set_pixel_format(mtl_format); + descriptor.set_usage(conv::map_texture_usage(desc.usage)); + descriptor.set_storage_mode(mtl::MTLStorageMode::Private); + + let raw = self.shared.device.lock().new_texture(&descriptor); + if let Some(label) = desc.label { + raw.set_label(label); + } + + Ok(super::Texture { + raw, + raw_format: mtl_format, + raw_type: mtl_type, + mip_levels: desc.mip_level_count, + array_layers, + }) + } + + unsafe fn destroy_texture(&self, _texture: super::Texture) {} + + unsafe fn create_texture_view( + &self, + texture: &super::Texture, + desc: &crate::TextureViewDescriptor, + ) -> DeviceResult { + let raw_format = self.shared.private_caps.map_format(desc.format); + + let raw_type = if texture.raw_type == mtl::MTLTextureType::D2Multisample { + texture.raw_type + } else { + conv::map_texture_view_dimension(desc.dimension) + }; + + let raw = if raw_format == texture.raw_format + && raw_type == texture.raw_type + && desc.range == wgt::ImageSubresourceRange::default() + { + // Some images are marked as framebuffer-only, and we can't create aliases of them. + // Also helps working around Metal bugs with aliased array textures. + texture.raw.to_owned() + } else { + let mip_level_count = match desc.range.mip_level_count { + Some(count) => count.get(), + None => texture.mip_levels - desc.range.base_mip_level, + }; + let array_layer_count = match desc.range.array_layer_count { + Some(count) => count.get(), + None => texture.array_layers - desc.range.base_array_layer, + }; + + let raw = texture.raw.new_texture_view_from_slice( + raw_format, + raw_type, + mtl::NSRange { + location: desc.range.base_mip_level as _, + length: mip_level_count as _, + }, + mtl::NSRange { + location: desc.range.base_array_layer as _, + length: array_layer_count as _, + }, + ); + if let Some(label) = desc.label { + raw.set_label(label); + } + raw + }; + + let aspects = crate::FormatAspect::from(desc.format); + Ok(super::TextureView { raw, aspects }) + } + unsafe fn destroy_texture_view(&self, _view: super::TextureView) {} + + unsafe fn create_sampler( + &self, + desc: &crate::SamplerDescriptor, + ) -> DeviceResult { + let caps = &self.shared.private_caps; + let descriptor = mtl::SamplerDescriptor::new(); + + descriptor.set_min_filter(conv::map_filter_mode(desc.min_filter)); + descriptor.set_mag_filter(conv::map_filter_mode(desc.mag_filter)); + descriptor.set_mip_filter(match desc.mipmap_filter { + wgt::FilterMode::Nearest if desc.lod_clamp.is_none() => { + mtl::MTLSamplerMipFilter::NotMipmapped + } + wgt::FilterMode::Nearest => mtl::MTLSamplerMipFilter::Nearest, + wgt::FilterMode::Linear => mtl::MTLSamplerMipFilter::Linear, + }); + + if let Some(aniso) = desc.anisotropy_clamp { + descriptor.set_max_anisotropy(aniso.get() as _); + } + + let [s, t, r] = desc.address_modes; + descriptor.set_address_mode_s(conv::map_address_mode(s)); + descriptor.set_address_mode_t(conv::map_address_mode(t)); + descriptor.set_address_mode_r(conv::map_address_mode(r)); + + if let Some(ref range) = desc.lod_clamp { + descriptor.set_lod_min_clamp(range.start); + descriptor.set_lod_max_clamp(range.end); + } + + if caps.sampler_lod_average { + descriptor.set_lod_average(true); // optimization + } + + if let Some(fun) = desc.compare { + descriptor.set_compare_function(conv::map_compare_function(fun)); + } + if let Some(border_color) = desc.border_color { + descriptor.set_border_color(conv::map_border_color(border_color)); + } + + if let Some(label) = desc.label { + descriptor.set_label(label); + } + let raw = self.shared.device.lock().new_sampler(&descriptor); + + Ok(super::Sampler { raw }) + } + unsafe fn destroy_sampler(&self, _sampler: super::Sampler) {} + + unsafe fn create_command_encoder( + &self, + desc: &crate::CommandEncoderDescriptor, + ) -> Result { + Ok(super::CommandEncoder { + shared: Arc::clone(&self.shared), + raw_queue: Arc::clone(&desc.queue.raw), + raw_cmd_buf: None, + state: super::CommandState::default(), + temp: super::Temp::default(), + }) + } + unsafe fn destroy_command_encoder(&self, _encoder: super::CommandEncoder) {} + + unsafe fn create_bind_group_layout( + &self, + desc: &crate::BindGroupLayoutDescriptor, + ) -> DeviceResult { + let mut map = desc.entries.to_vec(); + map.sort_by_key(|e| e.binding); + + Ok(super::BindGroupLayout { + entries: Arc::new(map), + }) + } + unsafe fn destroy_bind_group_layout(&self, _bg_layout: super::BindGroupLayout) {} + + unsafe fn create_pipeline_layout( + &self, + desc: &crate::PipelineLayoutDescriptor, + ) -> DeviceResult { + #[derive(Debug)] + struct StageInfo { + stage: naga::ShaderStage, + counters: super::ResourceData, + pc_buffer: Option, + pc_limit: u32, + sizes_buffer: Option, + sizes_count: u8, + } + + let mut stage_data = super::NAGA_STAGES.map(|&stage| StageInfo { + stage, + counters: super::ResourceData::default(), + pc_buffer: None, + pc_limit: 0, + sizes_buffer: None, + sizes_count: 0, + }); + let mut binding_map = std::collections::BTreeMap::default(); + let mut bind_group_infos = arrayvec::ArrayVec::new(); + + // First, place the push constants + for info in stage_data.iter_mut() { + for pcr in desc.push_constant_ranges { + if pcr.stages.contains(map_naga_stage(info.stage)) { + debug_assert_eq!(pcr.range.end % 4, 0); + info.pc_limit = (pcr.range.end / 4).max(info.pc_limit); + } + } + + // round up the limits alignment to 4, so that it matches MTL compiler logic + const LIMIT_MASK: u32 = 3; + //TODO: figure out what and how exactly does the alignment. Clearly, it's not + // straightforward, given that value of 2 stays non-aligned. + if info.pc_limit > LIMIT_MASK { + info.pc_limit = (info.pc_limit + LIMIT_MASK) & !LIMIT_MASK; + } + + // handle the push constant buffer assignment and shader overrides + if info.pc_limit != 0 { + info.pc_buffer = Some(info.counters.buffers); + info.counters.buffers += 1; + } + } + + // Second, place the described resources + for (group_index, &bgl) in desc.bind_group_layouts.iter().enumerate() { + // remember where the resources for this set start at each shader stage + let mut dynamic_buffers = Vec::new(); + let base_resource_indices = stage_data.map(|info| info.counters.clone()); + + for entry in bgl.entries.iter() { + if let wgt::BindingType::Buffer { + ty, + has_dynamic_offset, + min_binding_size: _, + } = entry.ty + { + if has_dynamic_offset { + dynamic_buffers.push(stage_data.map(|info| { + if entry.visibility.contains(map_naga_stage(info.stage)) { + info.counters.buffers + } else { + !0 + } + })); + } + if let wgt::BufferBindingType::Storage { .. } = ty { + for info in stage_data.iter_mut() { + if entry.visibility.contains(map_naga_stage(info.stage)) { + info.sizes_count += 1; + } + } + } + } + + for info in stage_data.iter_mut() { + if !entry.visibility.contains(map_naga_stage(info.stage)) { + continue; + } + + let mut target = naga::back::msl::BindTarget::default(); + match entry.ty { + wgt::BindingType::Buffer { ty, .. } => { + target.buffer = Some(info.counters.buffers as _); + info.counters.buffers += 1; + if let wgt::BufferBindingType::Storage { read_only } = ty { + target.mutable = !read_only; + } + } + wgt::BindingType::Sampler { .. } => { + target.sampler = Some(naga::back::msl::BindSamplerTarget::Resource( + info.counters.samplers as _, + )); + info.counters.samplers += 1; + } + wgt::BindingType::Texture { .. } => { + target.texture = Some(info.counters.textures as _); + info.counters.textures += 1; + } + wgt::BindingType::StorageTexture { access, .. } => { + target.texture = Some(info.counters.textures as _); + info.counters.textures += 1; + target.mutable = match access { + wgt::StorageTextureAccess::ReadOnly => false, + wgt::StorageTextureAccess::WriteOnly => true, + wgt::StorageTextureAccess::ReadWrite => true, + }; + } + } + + let source = naga::back::msl::BindSource { + stage: info.stage, + group: group_index as u32, + binding: entry.binding, + }; + binding_map.insert(source, target); + } + } + + bind_group_infos.push(super::BindGroupLayoutInfo { + base_resource_indices, + }); + } + + // Finally, make sure we fit the limits + for info in stage_data.iter_mut() { + // handle the sizes buffer assignment and shader overrides + if info.sizes_count != 0 { + info.sizes_buffer = Some(info.counters.buffers); + info.counters.buffers += 1; + } + if info.counters.buffers > self.shared.private_caps.max_buffers_per_stage + || info.counters.textures > self.shared.private_caps.max_textures_per_stage + || info.counters.samplers > self.shared.private_caps.max_samplers_per_stage + { + log::error!("Resource limit exceeded: {:?}", info); + return Err(crate::DeviceError::OutOfMemory); + } + } + + let per_stage_map = stage_data.map(|info| naga::back::msl::PerStageResources { + push_constant_buffer: info + .pc_buffer + .map(|buffer_index| buffer_index as naga::back::msl::Slot), + sizes_buffer: info + .sizes_buffer + .map(|buffer_index| buffer_index as naga::back::msl::Slot), + }); + + let naga_options = naga::back::msl::Options { + lang_version: match self.shared.private_caps.msl_version { + mtl::MTLLanguageVersion::V1_0 => (1, 0), + mtl::MTLLanguageVersion::V1_1 => (1, 1), + mtl::MTLLanguageVersion::V1_2 => (1, 2), + mtl::MTLLanguageVersion::V2_0 => (2, 0), + mtl::MTLLanguageVersion::V2_1 => (2, 1), + mtl::MTLLanguageVersion::V2_2 => (2, 2), + mtl::MTLLanguageVersion::V2_3 => (2, 3), + }, + binding_map, + inline_samplers: Default::default(), + spirv_cross_compatibility: false, + fake_missing_bindings: false, + per_stage_map: naga::back::msl::PerStageMap { + vs: per_stage_map.vs, + fs: per_stage_map.fs, + cs: per_stage_map.cs, + }, + }; + + Ok(super::PipelineLayout { + naga_options, + bind_group_infos, + push_constants_infos: stage_data.map(|info| { + info.pc_buffer.map(|buffer_index| super::PushConstantsInfo { + count: info.pc_limit, + buffer_index, + }) + }), + total_counters: stage_data.map(|info| info.counters.clone()), + }) + } + unsafe fn destroy_pipeline_layout(&self, _pipeline_layout: super::PipelineLayout) {} + + unsafe fn create_bind_group( + &self, + desc: &crate::BindGroupDescriptor, + ) -> DeviceResult { + let mut bg = super::BindGroup::default(); + for (&stage, counter) in super::NAGA_STAGES.iter().zip(bg.counters.iter_mut()) { + let stage_bit = map_naga_stage(stage); + let mut dynamic_offsets_count = 0u32; + for (entry, layout) in desc.entries.iter().zip(desc.layout.entries.iter()) { + if let wgt::BindingType::Buffer { + has_dynamic_offset: true, + .. + } = layout.ty + { + dynamic_offsets_count += 1; + } + if !layout.visibility.contains(stage_bit) { + continue; + } + match layout.ty { + wgt::BindingType::Buffer { + ty, + has_dynamic_offset, + .. + } => { + let source = &desc.buffers[entry.resource_index as usize]; + let remaining_size = + wgt::BufferSize::new(source.buffer.size - source.offset); + let binding_size = match ty { + wgt::BufferBindingType::Storage { .. } => { + source.size.or(remaining_size) + } + _ => None, + }; + bg.buffers.push(super::BufferResource { + ptr: source.buffer.as_raw(), + offset: source.offset, + dynamic_index: if has_dynamic_offset { + Some(dynamic_offsets_count - 1) + } else { + None + }, + binding_size, + binding_location: layout.binding, + }); + counter.buffers += 1; + } + wgt::BindingType::Sampler { .. } => { + let res = desc.samplers[entry.resource_index as usize].as_raw(); + bg.samplers.push(res); + counter.samplers += 1; + } + wgt::BindingType::Texture { .. } | wgt::BindingType::StorageTexture { .. } => { + let res = desc.textures[entry.resource_index as usize].view.as_raw(); + bg.textures.push(res); + counter.textures += 1; + } + } + } + } + + Ok(bg) + } + + unsafe fn destroy_bind_group(&self, _group: super::BindGroup) {} + + unsafe fn create_shader_module( + &self, + _desc: &crate::ShaderModuleDescriptor, + shader: crate::NagaShader, + ) -> Result { + Ok(super::ShaderModule { raw: shader }) + } + unsafe fn destroy_shader_module(&self, _module: super::ShaderModule) {} + + unsafe fn create_render_pipeline( + &self, + desc: &crate::RenderPipelineDescriptor, + ) -> Result { + let descriptor = mtl::RenderPipelineDescriptor::new(); + let (primitive_class, raw_primitive_type) = + conv::map_primitive_topology(desc.primitive.topology); + + let vs = self.load_shader( + &desc.vertex_stage, + desc.layout, + primitive_class, + naga::ShaderStage::Vertex, + )?; + + descriptor.set_vertex_function(Some(&vs.function)); + + // Fragment shader + let (fs_lib, fs_sized_bindings) = match desc.fragment_stage { + Some(ref stage) => { + let compiled = self.load_shader( + stage, + desc.layout, + primitive_class, + naga::ShaderStage::Fragment, + )?; + descriptor.set_fragment_function(Some(&compiled.function)); + (Some(compiled.library), compiled.sized_bindings) + } + None => { + // TODO: This is a workaround for what appears to be a Metal validation bug + // A pixel format is required even though no attachments are provided + if desc.color_targets.is_empty() && desc.depth_stencil.is_none() { + descriptor.set_depth_attachment_pixel_format(mtl::MTLPixelFormat::Depth32Float); + } + (None, Vec::new()) + } + }; + + for (i, ct) in desc.color_targets.iter().enumerate() { + let at_descriptor = descriptor.color_attachments().object_at(i as u64).unwrap(); + + let raw_format = self.shared.private_caps.map_format(ct.format); + at_descriptor.set_pixel_format(raw_format); + at_descriptor.set_write_mask(conv::map_color_write(ct.write_mask)); + + if let Some(ref blend) = ct.blend { + at_descriptor.set_blending_enabled(true); + let (color_op, color_src, color_dst) = conv::map_blend_component(&blend.color); + let (alpha_op, alpha_src, alpha_dst) = conv::map_blend_component(&blend.alpha); + + at_descriptor.set_rgb_blend_operation(color_op); + at_descriptor.set_source_rgb_blend_factor(color_src); + at_descriptor.set_destination_rgb_blend_factor(color_dst); + + at_descriptor.set_alpha_blend_operation(alpha_op); + at_descriptor.set_source_alpha_blend_factor(alpha_src); + at_descriptor.set_destination_alpha_blend_factor(alpha_dst); + } + } + + let depth_stencil = match desc.depth_stencil { + Some(ref ds) => { + let raw_format = self.shared.private_caps.map_format(ds.format); + let aspects = crate::FormatAspect::from(ds.format); + if aspects.contains(crate::FormatAspect::DEPTH) { + descriptor.set_depth_attachment_pixel_format(raw_format); + } + if aspects.contains(crate::FormatAspect::STENCIL) { + descriptor.set_stencil_attachment_pixel_format(raw_format); + } + + let ds_descriptor = create_depth_stencil_desc(ds); + let raw = self + .shared + .device + .lock() + .new_depth_stencil_state(&ds_descriptor); + Some((raw, ds.bias)) + } + None => None, + }; + + if desc.layout.total_counters.vs.buffers + (desc.vertex_buffers.len() as u32) + > self.shared.private_caps.max_buffers_per_stage + { + let msg = format!( + "pipeline needs too many buffers in the vertex stage: {} vertex and {} layout", + desc.vertex_buffers.len(), + desc.layout.total_counters.vs.buffers + ); + return Err(crate::PipelineError::Linkage(wgt::ShaderStage::VERTEX, msg)); + } + + if !desc.vertex_buffers.is_empty() { + let vertex_descriptor = mtl::VertexDescriptor::new(); + for (i, vb) in desc.vertex_buffers.iter().enumerate() { + let buffer_index = + self.shared.private_caps.max_buffers_per_stage as u64 - 1 - i as u64; + let buffer_desc = vertex_descriptor.layouts().object_at(buffer_index).unwrap(); + + buffer_desc.set_stride(vb.array_stride); + buffer_desc.set_step_function(conv::map_step_mode(vb.step_mode)); + + for at in vb.attributes { + let attribute_desc = vertex_descriptor + .attributes() + .object_at(at.shader_location as u64) + .unwrap(); + attribute_desc.set_format(conv::map_vertex_format(at.format)); + attribute_desc.set_buffer_index(buffer_index); + attribute_desc.set_offset(at.offset); + } + } + descriptor.set_vertex_descriptor(Some(&vertex_descriptor)); + } + + if desc.multisample.count != 1 { + //TODO: handle sample mask + descriptor.set_sample_count(desc.multisample.count as u64); + descriptor.set_alpha_to_coverage_enabled(desc.multisample.alpha_to_coverage_enabled); + //descriptor.set_alpha_to_one_enabled(desc.multisample.alpha_to_one_enabled); + } + + if let Some(name) = desc.label { + descriptor.set_label(name); + } + + let raw = self + .shared + .device + .lock() + .new_render_pipeline_state(&descriptor) + .map_err(|e| { + crate::PipelineError::Linkage( + wgt::ShaderStage::VERTEX | wgt::ShaderStage::FRAGMENT, + format!("new_render_pipeline_state: {:?}", e), + ) + })?; + + Ok(super::RenderPipeline { + raw, + vs_lib: vs.library, + fs_lib, + vs_info: super::PipelineStageInfo { + push_constants: desc.layout.push_constants_infos.vs, + sizes_slot: desc.layout.naga_options.per_stage_map.vs.sizes_buffer, + sized_bindings: vs.sized_bindings, + }, + fs_info: super::PipelineStageInfo { + push_constants: desc.layout.push_constants_infos.fs, + sizes_slot: desc.layout.naga_options.per_stage_map.fs.sizes_buffer, + sized_bindings: fs_sized_bindings, + }, + raw_primitive_type, + raw_front_winding: conv::map_winding(desc.primitive.front_face), + raw_cull_mode: conv::map_cull_mode(desc.primitive.cull_mode), + raw_depth_clip_mode: if self.features.contains(wgt::Features::DEPTH_CLAMPING) { + Some(if desc.primitive.clamp_depth { + mtl::MTLDepthClipMode::Clamp + } else { + mtl::MTLDepthClipMode::Clip + }) + } else { + None + }, + depth_stencil, + }) + } + unsafe fn destroy_render_pipeline(&self, _pipeline: super::RenderPipeline) {} + + unsafe fn create_compute_pipeline( + &self, + desc: &crate::ComputePipelineDescriptor, + ) -> Result { + let descriptor = mtl::ComputePipelineDescriptor::new(); + + let cs = self.load_shader( + &desc.stage, + desc.layout, + mtl::MTLPrimitiveTopologyClass::Unspecified, + naga::ShaderStage::Compute, + )?; + descriptor.set_compute_function(Some(&cs.function)); + + if let Some(name) = desc.label { + descriptor.set_label(name); + } + + let raw = self + .shared + .device + .lock() + .new_compute_pipeline_state(&descriptor) + .map_err(|e| { + crate::PipelineError::Linkage( + wgt::ShaderStage::COMPUTE, + format!("new_compute_pipeline_state: {:?}", e), + ) + })?; + + Ok(super::ComputePipeline { + raw, + cs_info: super::PipelineStageInfo { + push_constants: desc.layout.push_constants_infos.cs, + sizes_slot: desc.layout.naga_options.per_stage_map.cs.sizes_buffer, + sized_bindings: cs.sized_bindings, + }, + cs_lib: cs.library, + work_group_size: cs.wg_size, + }) + } + unsafe fn destroy_compute_pipeline(&self, _pipeline: super::ComputePipeline) {} + + unsafe fn create_query_set( + &self, + desc: &wgt::QuerySetDescriptor, + ) -> DeviceResult { + match desc.ty { + wgt::QueryType::Occlusion => { + let size = desc.count as u64 * crate::QUERY_SIZE; + let options = mtl::MTLResourceOptions::empty(); + //TODO: HazardTrackingModeUntracked + let raw_buffer = self.shared.device.lock().new_buffer(size, options); + if let Some(label) = desc.label { + raw_buffer.set_label(label); + } + Ok(super::QuerySet { + raw_buffer, + ty: desc.ty, + }) + } + wgt::QueryType::Timestamp | wgt::QueryType::PipelineStatistics(_) => { + Err(crate::DeviceError::OutOfMemory) + } + } + } + unsafe fn destroy_query_set(&self, _set: super::QuerySet) {} + + unsafe fn create_fence(&self) -> DeviceResult { + Ok(super::Fence { + completed_value: Arc::new(atomic::AtomicU64::new(0)), + pending_command_buffers: Vec::new(), + }) + } + unsafe fn destroy_fence(&self, _fence: super::Fence) {} + unsafe fn get_fence_value(&self, fence: &super::Fence) -> DeviceResult { + let mut max_value = fence.completed_value.load(atomic::Ordering::Acquire); + for &(value, ref cmd_buf) in fence.pending_command_buffers.iter() { + if cmd_buf.status() == mtl::MTLCommandBufferStatus::Completed { + max_value = value; + } + } + Ok(max_value) + } + unsafe fn wait( + &self, + fence: &super::Fence, + wait_value: crate::FenceValue, + timeout_ms: u32, + ) -> DeviceResult { + if wait_value <= fence.completed_value.load(atomic::Ordering::Acquire) { + return Ok(true); + } + + let cmd_buf = match fence + .pending_command_buffers + .iter() + .find(|&&(value, _)| value >= wait_value) + { + Some(&(_, ref cmd_buf)) => cmd_buf, + None => { + log::error!("No active command buffers for fence value {}", wait_value); + return Err(crate::DeviceError::Lost); + } + }; + + let start = time::Instant::now(); + loop { + if let mtl::MTLCommandBufferStatus::Completed = cmd_buf.status() { + return Ok(true); + } + if start.elapsed().as_millis() >= timeout_ms as u128 { + return Ok(false); + } + thread::sleep(time::Duration::from_millis(1)); + } + } + + unsafe fn start_capture(&self) -> bool { + if !self.shared.private_caps.supports_capture_manager { + return false; + } + let device = self.shared.device.lock(); + let shared_capture_manager = mtl::CaptureManager::shared(); + let default_capture_scope = shared_capture_manager.new_capture_scope_with_device(&device); + shared_capture_manager.set_default_capture_scope(&default_capture_scope); + shared_capture_manager.start_capture_with_scope(&default_capture_scope); + default_capture_scope.begin_scope(); + true + } + unsafe fn stop_capture(&self) { + let shared_capture_manager = mtl::CaptureManager::shared(); + if let Some(default_capture_scope) = shared_capture_manager.default_capture_scope() { + default_capture_scope.end_scope(); + } + shared_capture_manager.stop_capture(); + } +} diff --git a/wgpu-hal/src/metal/mod.rs b/wgpu-hal/src/metal/mod.rs new file mode 100644 index 0000000000..b7384286db --- /dev/null +++ b/wgpu-hal/src/metal/mod.rs @@ -0,0 +1,687 @@ +mod adapter; +mod command; +mod conv; +mod device; +mod surface; + +use std::{ + iter, ops, + ptr::NonNull, + sync::{atomic, Arc}, + thread, +}; + +use arrayvec::ArrayVec; +use foreign_types::ForeignTypeRef as _; +use parking_lot::Mutex; + +#[derive(Clone)] +pub struct Api; + +type ResourceIndex = u32; + +impl crate::Api for Api { + type Instance = Instance; + type Surface = Surface; + type Adapter = Adapter; + type Device = Device; + + type Queue = Queue; + type CommandEncoder = CommandEncoder; + type CommandBuffer = CommandBuffer; + + type Buffer = Buffer; + type Texture = Texture; + type SurfaceTexture = SurfaceTexture; + type TextureView = TextureView; + type Sampler = Sampler; + type QuerySet = QuerySet; + type Fence = Fence; + + type BindGroupLayout = BindGroupLayout; + type BindGroup = BindGroup; + type PipelineLayout = PipelineLayout; + type ShaderModule = ShaderModule; + type RenderPipeline = RenderPipeline; + type ComputePipeline = ComputePipeline; +} + +pub struct Instance {} + +impl crate::Instance for Instance { + unsafe fn init(_desc: &crate::InstanceDescriptor) -> Result { + //TODO: enable `METAL_DEVICE_WRAPPER_TYPE` environment based on the flags? + Ok(Instance {}) + } + + unsafe fn create_surface( + &self, + has_handle: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result { + match has_handle.raw_window_handle() { + #[cfg(target_os = "ios")] + raw_window_handle::RawWindowHandle::IOS(handle) => { + Ok(Surface::from_uiview(handle.ui_view)) + } + #[cfg(target_os = "macos")] + raw_window_handle::RawWindowHandle::MacOS(handle) => { + Ok(Surface::from_nsview(handle.ns_view)) + } + _ => Err(crate::InstanceError), + } + } + + unsafe fn destroy_surface(&self, surface: Surface) { + surface.dispose(); + } + + unsafe fn enumerate_adapters(&self) -> Vec> { + let devices = mtl::Device::all(); + let mut adapters: Vec> = devices + .into_iter() + .map(|dev| { + let name = dev.name().into(); + let shared = AdapterShared::new(dev); + crate::ExposedAdapter { + info: wgt::AdapterInfo { + name, + vendor: 0, + device: 0, + device_type: if shared.private_caps.low_power { + wgt::DeviceType::IntegratedGpu + } else { + wgt::DeviceType::DiscreteGpu + }, + backend: wgt::Backend::Metal, + }, + features: shared.private_caps.features(), + capabilities: shared.private_caps.capabilities(), + adapter: Adapter::new(Arc::new(shared)), + } + }) + .collect(); + adapters.sort_by_key(|ad| { + ( + ad.adapter.shared.private_caps.low_power, + ad.adapter.shared.private_caps.headless, + ) + }); + adapters + } +} + +#[derive(Clone, Debug)] +struct PrivateCapabilities { + family_check: bool, + msl_version: mtl::MTLLanguageVersion, + exposed_queues: usize, + read_write_texture_tier: mtl::MTLReadWriteTextureTier, + resource_heaps: bool, + argument_buffers: bool, + shared_textures: bool, + mutable_comparison_samplers: bool, + sampler_clamp_to_border: bool, + sampler_lod_average: bool, + base_instance: bool, + base_vertex_instance_drawing: bool, + dual_source_blending: bool, + low_power: bool, + headless: bool, + layered_rendering: bool, + function_specialization: bool, + depth_clip_mode: bool, + texture_cube_array: bool, + format_depth24_stencil8: bool, + format_depth32_stencil8_filter: bool, + format_depth32_stencil8_none: bool, + format_min_srgb_channels: u8, + format_b5: bool, + format_bc: bool, + format_eac_etc: bool, + format_astc: bool, + format_any8_unorm_srgb_all: bool, + format_any8_unorm_srgb_no_write: bool, + format_any8_snorm_all: bool, + format_r16_norm_all: bool, + format_r32_all: bool, + format_r32_no_write: bool, + format_r32float_no_write_no_filter: bool, + format_r32float_no_filter: bool, + format_r32float_all: bool, + format_rgba8_srgb_all: bool, + format_rgba8_srgb_no_write: bool, + format_rgb10a2_unorm_all: bool, + format_rgb10a2_unorm_no_write: bool, + format_rgb10a2_uint_color: bool, + format_rgb10a2_uint_color_write: bool, + format_rg11b10_all: bool, + format_rg11b10_no_write: bool, + format_rgb9e5_all: bool, + format_rgb9e5_no_write: bool, + format_rgb9e5_filter_only: bool, + format_rg32_color: bool, + format_rg32_color_write: bool, + format_rg32float_all: bool, + format_rg32float_color_blend: bool, + format_rg32float_no_filter: bool, + format_rgba32int_color: bool, + format_rgba32int_color_write: bool, + format_rgba32float_color: bool, + format_rgba32float_color_write: bool, + format_rgba32float_all: bool, + format_depth16unorm: bool, + format_depth32float_filter: bool, + format_depth32float_none: bool, + format_bgr10a2_all: bool, + format_bgr10a2_no_write: bool, + max_buffers_per_stage: ResourceIndex, + max_textures_per_stage: ResourceIndex, + max_samplers_per_stage: ResourceIndex, + buffer_alignment: u64, + max_buffer_size: u64, + max_texture_size: u64, + max_texture_3d_size: u64, + max_texture_layers: u64, + max_fragment_input_components: u64, + max_color_render_targets: u8, + max_total_threadgroup_memory: u32, + sample_count_mask: u8, + supports_debug_markers: bool, + supports_binary_archives: bool, + supports_capture_manager: bool, + can_set_maximum_drawables_count: bool, + can_set_display_sync: bool, + can_set_next_drawable_timeout: bool, +} + +#[derive(Clone, Debug)] +struct PrivateDisabilities { + /// Near depth is not respected properly on some Intel GPUs. + broken_viewport_near_depth: bool, + /// Multi-target clears don't appear to work properly on Intel GPUs. + broken_layered_clear_image: bool, +} + +#[derive(Debug, Default)] +struct Settings { + retain_command_buffer_references: bool, +} + +struct AdapterShared { + device: Mutex, + disabilities: PrivateDisabilities, + private_caps: PrivateCapabilities, + settings: Settings, +} + +unsafe impl Send for AdapterShared {} +unsafe impl Sync for AdapterShared {} + +impl AdapterShared { + fn new(device: mtl::Device) -> Self { + let private_caps = PrivateCapabilities::new(&device); + log::debug!("{:#?}", private_caps); + + Self { + disabilities: PrivateDisabilities::new(&device), + private_caps: PrivateCapabilities::new(&device), + device: Mutex::new(device), + settings: Settings::default(), + } + } +} + +pub struct Adapter { + shared: Arc, +} + +pub struct Queue { + raw: Arc>, +} + +unsafe impl Send for Queue {} +unsafe impl Sync for Queue {} + +pub struct Device { + shared: Arc, + features: wgt::Features, +} + +pub struct Surface { + view: Option>, + render_layer: Mutex, + raw_swapchain_format: mtl::MTLPixelFormat, + main_thread_id: thread::ThreadId, + // Useful for UI-intensive applications that are sensitive to + // window resizing. + pub present_with_transaction: bool, +} + +unsafe impl Send for Surface {} +unsafe impl Sync for Surface {} + +#[derive(Debug)] +pub struct SurfaceTexture { + texture: Texture, + drawable: mtl::MetalDrawable, + present_with_transaction: bool, +} + +impl std::borrow::Borrow for SurfaceTexture { + fn borrow(&self) -> &Texture { + &self.texture + } +} + +unsafe impl Send for SurfaceTexture {} +unsafe impl Sync for SurfaceTexture {} + +impl crate::Queue for Queue { + unsafe fn submit( + &mut self, + command_buffers: &[&CommandBuffer], + signal_fence: Option<(&mut Fence, crate::FenceValue)>, + ) -> Result<(), crate::DeviceError> { + objc::rc::autoreleasepool(|| { + let extra_command_buffer = match signal_fence { + Some((fence, value)) => { + let completed_value = Arc::clone(&fence.completed_value); + let block = block::ConcreteBlock::new(move |_cmd_buf| { + completed_value.store(value, atomic::Ordering::Release); + }) + .copy(); + + let raw = match command_buffers.last() { + Some(&cmd_buf) => cmd_buf.raw.to_owned(), + None => { + let queue = self.raw.lock(); + queue + .new_command_buffer_with_unretained_references() + .to_owned() + } + }; + raw.set_label("_Signal"); + raw.add_completed_handler(&block); + + fence.maintain(); + fence.pending_command_buffers.push((value, raw.to_owned())); + // only return an extra one if it's extra + match command_buffers.last() { + Some(_) => None, + None => Some(raw), + } + } + None => None, + }; + + for cmd_buffer in command_buffers { + cmd_buffer.raw.commit(); + } + + if let Some(raw) = extra_command_buffer { + raw.commit(); + } + }); + Ok(()) + } + unsafe fn present( + &mut self, + _surface: &mut Surface, + texture: SurfaceTexture, + ) -> Result<(), crate::SurfaceError> { + let queue = &self.raw.lock(); + objc::rc::autoreleasepool(|| { + let command_buffer = queue.new_command_buffer(); + command_buffer.set_label("_Present"); + + // https://developer.apple.com/documentation/quartzcore/cametallayer/1478157-presentswithtransaction?language=objc + if !texture.present_with_transaction { + command_buffer.present_drawable(&texture.drawable); + } + + command_buffer.commit(); + + if texture.present_with_transaction { + command_buffer.wait_until_scheduled(); + texture.drawable.present(); + } + }); + Ok(()) + } +} + +#[derive(Debug)] +pub struct Buffer { + raw: mtl::Buffer, + size: wgt::BufferAddress, + options: mtl::MTLResourceOptions, +} + +unsafe impl Send for Buffer {} +unsafe impl Sync for Buffer {} + +impl Buffer { + fn as_raw(&self) -> BufferPtr { + unsafe { NonNull::new_unchecked(self.raw.as_ptr()) } + } +} + +#[derive(Debug)] +pub struct Texture { + raw: mtl::Texture, + raw_format: mtl::MTLPixelFormat, + raw_type: mtl::MTLTextureType, + array_layers: u32, + mip_levels: u32, +} + +unsafe impl Send for Texture {} +unsafe impl Sync for Texture {} + +#[derive(Debug)] +pub struct TextureView { + raw: mtl::Texture, + aspects: crate::FormatAspect, +} + +unsafe impl Send for TextureView {} +unsafe impl Sync for TextureView {} + +impl TextureView { + fn as_raw(&self) -> TexturePtr { + unsafe { NonNull::new_unchecked(self.raw.as_ptr()) } + } +} + +#[derive(Debug)] +pub struct Sampler { + raw: mtl::SamplerState, +} + +unsafe impl Send for Sampler {} +unsafe impl Sync for Sampler {} + +impl Sampler { + fn as_raw(&self) -> SamplerPtr { + unsafe { NonNull::new_unchecked(self.raw.as_ptr()) } + } +} + +#[derive(Debug)] +pub struct BindGroupLayout { + /// Sorted list of BGL entries. + entries: Arc>, +} + +#[derive(Clone, Debug, Default)] +struct ResourceData { + buffers: T, + textures: T, + samplers: T, +} + +#[derive(Clone, Debug, Default)] +struct MultiStageData { + vs: T, + fs: T, + cs: T, +} + +const NAGA_STAGES: MultiStageData = MultiStageData { + vs: naga::ShaderStage::Vertex, + fs: naga::ShaderStage::Fragment, + cs: naga::ShaderStage::Compute, +}; + +impl ops::Index for MultiStageData { + type Output = T; + fn index(&self, stage: naga::ShaderStage) -> &T { + match stage { + naga::ShaderStage::Vertex => &self.vs, + naga::ShaderStage::Fragment => &self.fs, + naga::ShaderStage::Compute => &self.cs, + } + } +} + +impl MultiStageData { + fn map(&self, fun: impl Fn(&T) -> Y) -> MultiStageData { + MultiStageData { + vs: fun(&self.vs), + fs: fun(&self.fs), + cs: fun(&self.cs), + } + } + fn iter<'a>(&'a self) -> impl Iterator { + iter::once(&self.vs) + .chain(iter::once(&self.fs)) + .chain(iter::once(&self.cs)) + } + fn iter_mut<'a>(&'a mut self) -> impl Iterator { + iter::once(&mut self.vs) + .chain(iter::once(&mut self.fs)) + .chain(iter::once(&mut self.cs)) + } +} + +type MultiStageResourceCounters = MultiStageData>; + +#[derive(Debug)] +struct BindGroupLayoutInfo { + base_resource_indices: MultiStageResourceCounters, +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +struct PushConstantsInfo { + count: u32, + buffer_index: ResourceIndex, +} + +#[derive(Debug)] +pub struct PipelineLayout { + naga_options: naga::back::msl::Options, + bind_group_infos: ArrayVec<[BindGroupLayoutInfo; crate::MAX_BIND_GROUPS]>, + push_constants_infos: MultiStageData>, + total_counters: MultiStageResourceCounters, +} + +trait AsNative { + type Native; + fn from(native: &Self::Native) -> Self; + fn as_native(&self) -> &Self::Native; +} + +type BufferPtr = NonNull; +type TexturePtr = NonNull; +type SamplerPtr = NonNull; + +impl AsNative for BufferPtr { + type Native = mtl::BufferRef; + #[inline] + fn from(native: &Self::Native) -> Self { + unsafe { NonNull::new_unchecked(native.as_ptr()) } + } + #[inline] + fn as_native(&self) -> &Self::Native { + unsafe { Self::Native::from_ptr(self.as_ptr()) } + } +} + +impl AsNative for TexturePtr { + type Native = mtl::TextureRef; + #[inline] + fn from(native: &Self::Native) -> Self { + unsafe { NonNull::new_unchecked(native.as_ptr()) } + } + #[inline] + fn as_native(&self) -> &Self::Native { + unsafe { Self::Native::from_ptr(self.as_ptr()) } + } +} + +impl AsNative for SamplerPtr { + type Native = mtl::SamplerStateRef; + #[inline] + fn from(native: &Self::Native) -> Self { + unsafe { NonNull::new_unchecked(native.as_ptr()) } + } + #[inline] + fn as_native(&self) -> &Self::Native { + unsafe { Self::Native::from_ptr(self.as_ptr()) } + } +} + +#[derive(Debug)] +struct BufferResource { + ptr: BufferPtr, + offset: wgt::BufferAddress, + dynamic_index: Option, + binding_size: Option, + binding_location: u32, +} + +#[derive(Debug, Default)] +pub struct BindGroup { + counters: MultiStageResourceCounters, + buffers: Vec, + samplers: Vec, + textures: Vec, +} + +unsafe impl Send for BindGroup {} +unsafe impl Sync for BindGroup {} + +#[derive(Debug)] +pub struct ShaderModule { + raw: crate::NagaShader, +} + +#[derive(Debug, Default)] +struct PipelineStageInfo { + push_constants: Option, + sizes_slot: Option, + sized_bindings: Vec, +} + +impl PipelineStageInfo { + fn clear(&mut self) { + self.push_constants = None; + self.sizes_slot = None; + self.sized_bindings.clear(); + } + + fn assign_from(&mut self, other: &Self) { + self.push_constants = other.push_constants; + self.sizes_slot = other.sizes_slot; + self.sized_bindings.clear(); + self.sized_bindings.extend_from_slice(&other.sized_bindings); + } +} + +pub struct RenderPipeline { + raw: mtl::RenderPipelineState, + #[allow(dead_code)] + vs_lib: mtl::Library, + #[allow(dead_code)] + fs_lib: Option, + vs_info: PipelineStageInfo, + fs_info: PipelineStageInfo, + raw_primitive_type: mtl::MTLPrimitiveType, + raw_front_winding: mtl::MTLWinding, + raw_cull_mode: mtl::MTLCullMode, + raw_depth_clip_mode: Option, + depth_stencil: Option<(mtl::DepthStencilState, wgt::DepthBiasState)>, +} + +unsafe impl Send for RenderPipeline {} +unsafe impl Sync for RenderPipeline {} + +pub struct ComputePipeline { + raw: mtl::ComputePipelineState, + #[allow(dead_code)] + cs_lib: mtl::Library, + cs_info: PipelineStageInfo, + work_group_size: mtl::MTLSize, +} + +unsafe impl Send for ComputePipeline {} +unsafe impl Sync for ComputePipeline {} + +#[derive(Debug)] +pub struct QuerySet { + raw_buffer: mtl::Buffer, + ty: wgt::QueryType, +} + +unsafe impl Send for QuerySet {} +unsafe impl Sync for QuerySet {} + +#[derive(Debug)] +pub struct Fence { + completed_value: Arc, + /// The pending fence values have to be ascending. + pending_command_buffers: Vec<(crate::FenceValue, mtl::CommandBuffer)>, +} + +unsafe impl Send for Fence {} +unsafe impl Sync for Fence {} + +impl Fence { + fn get_latest(&self) -> crate::FenceValue { + let mut max_value = self.completed_value.load(atomic::Ordering::Acquire); + for &(value, ref cmd_buf) in self.pending_command_buffers.iter() { + if cmd_buf.status() == mtl::MTLCommandBufferStatus::Completed { + max_value = value; + } + } + max_value + } + + fn maintain(&mut self) { + let latest = self.get_latest(); + self.pending_command_buffers + .retain(|&(value, _)| value > latest); + } +} + +struct IndexState { + buffer_ptr: BufferPtr, + offset: wgt::BufferAddress, + stride: wgt::BufferAddress, + raw_type: mtl::MTLIndexType, +} + +#[derive(Default)] +struct Temp { + binding_sizes: Vec, +} + +struct CommandState { + blit: Option, + render: Option, + compute: Option, + raw_primitive_type: mtl::MTLPrimitiveType, + index: Option, + raw_wg_size: mtl::MTLSize, + stage_infos: MultiStageData, + //TODO: use `naga::ResourceBinding` for keys + storage_buffer_length_map: fxhash::FxHashMap<(u32, u32), wgt::BufferSize>, +} + +pub struct CommandEncoder { + shared: Arc, + raw_queue: Arc>, + raw_cmd_buf: Option, + state: CommandState, + temp: Temp, +} + +unsafe impl Send for CommandEncoder {} +unsafe impl Sync for CommandEncoder {} + +pub struct CommandBuffer { + raw: mtl::CommandBuffer, +} + +unsafe impl Send for CommandBuffer {} +unsafe impl Sync for CommandBuffer {} diff --git a/wgpu-hal/src/metal/surface.rs b/wgpu-hal/src/metal/surface.rs new file mode 100644 index 0000000000..264c8e0862 --- /dev/null +++ b/wgpu-hal/src/metal/surface.rs @@ -0,0 +1,250 @@ +use std::{mem, os::raw::c_void, ptr::NonNull, thread}; + +use core_graphics_types::{ + base::CGFloat, + geometry::{CGRect, CGSize}, +}; +use objc::{ + class, msg_send, + rc::autoreleasepool, + runtime::{Object, BOOL, YES}, + sel, sel_impl, +}; +use parking_lot::Mutex; + +#[link(name = "QuartzCore", kind = "framework")] +extern "C" { + #[allow(non_upper_case_globals)] + static kCAGravityTopLeft: *mut Object; +} + +impl super::Surface { + fn new(view: Option>, layer: mtl::MetalLayer) -> Self { + Self { + view, + render_layer: Mutex::new(layer), + raw_swapchain_format: mtl::MTLPixelFormat::Invalid, + main_thread_id: thread::current().id(), + present_with_transaction: false, + } + } + + pub unsafe fn dispose(self) { + if let Some(view) = self.view { + let () = msg_send![view.as_ptr(), release]; + } + } + + #[cfg(target_os = "ios")] + #[allow(clippy::transmute_ptr_to_ref)] + pub unsafe fn from_uiview(uiview: *mut c_void) -> Self { + let view = uiview as *mut Object; + if view.is_null() { + panic!("window does not have a valid contentView"); + } + + let main_layer: *mut Object = msg_send![view, layer]; + let class = class!(CAMetalLayer); + let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class]; + let render_layer = if is_valid_layer == YES { + mem::transmute::<_, &mtl::MetalLayerRef>(main_layer).to_owned() + } else { + // If the main layer is not a CAMetalLayer, we create a CAMetalLayer sublayer and use it instead. + // Unlike on macOS, we cannot replace the main view as UIView does not allow it (when NSView does). + let new_layer: mtl::MetalLayer = msg_send![class, new]; + let bounds: CGRect = msg_send![main_layer, bounds]; + let () = msg_send![new_layer.as_ref(), setFrame: bounds]; + let () = msg_send![main_layer, addSublayer: new_layer.as_ref()]; + new_layer + }; + + let window: *mut Object = msg_send![view, window]; + if !window.is_null() { + let screen: *mut Object = msg_send![window, screen]; + assert!(!screen.is_null(), "window is not attached to a screen"); + + let scale_factor: CGFloat = msg_send![screen, nativeScale]; + let () = msg_send![view, setContentScaleFactor: scale_factor]; + } + + let _: *mut c_void = msg_send![view, retain]; + Self::new(NonNull::new(view), render_layer) + } + + #[cfg(target_os = "macos")] + #[allow(clippy::transmute_ptr_to_ref)] + pub unsafe fn from_nsview(nsview: *mut c_void) -> Self { + let view = nsview as *mut Object; + if view.is_null() { + panic!("window does not have a valid contentView"); + } + + let class = class!(CAMetalLayer); + // Deprecated! Clients should use `create_surface_from_layer` instead. + let is_actually_layer: BOOL = msg_send![view, isKindOfClass: class]; + if is_actually_layer == YES { + return Self::from_layer(mem::transmute(view)); + } + + let existing: *mut Object = msg_send![view, layer]; + let use_current = if existing.is_null() { + false + } else { + let result: BOOL = msg_send![existing, isKindOfClass: class]; + result == YES + }; + + let render_layer: mtl::MetalLayer = if use_current { + mem::transmute::<_, &mtl::MetalLayerRef>(existing).to_owned() + } else { + let layer: mtl::MetalLayer = msg_send![class, new]; + let () = msg_send![view, setLayer: layer.as_ref()]; + let () = msg_send![view, setWantsLayer: YES]; + let bounds: CGRect = msg_send![view, bounds]; + let () = msg_send![layer.as_ref(), setBounds: bounds]; + + let window: *mut Object = msg_send![view, window]; + if !window.is_null() { + let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; + let () = msg_send![layer, setContentsScale: scale_factor]; + } + //let () = msg_send![layer, setDelegate: self.gfx_managed_metal_layer_delegate.0]; + layer + }; + + let () = msg_send![render_layer, setContentsGravity: kCAGravityTopLeft]; + + let _: *mut c_void = msg_send![view, retain]; + Self::new(NonNull::new(view), render_layer) + } + + pub unsafe fn from_layer(layer: &mtl::MetalLayerRef) -> Self { + let class = class!(CAMetalLayer); + let proper_kind: BOOL = msg_send![layer, isKindOfClass: class]; + assert_eq!(proper_kind, YES); + Self::new(None, layer.to_owned()) + } + + pub(super) fn dimensions(&self) -> wgt::Extent3d { + let (size, scale): (CGSize, CGFloat) = match self.view { + Some(view) if !cfg!(target_os = "macos") => unsafe { + let bounds: CGRect = msg_send![view.as_ptr(), bounds]; + let window: Option> = msg_send![view.as_ptr(), window]; + let screen = window.and_then(|window| -> Option> { + msg_send![window.as_ptr(), screen] + }); + match screen { + Some(screen) => { + let screen_space: *mut Object = msg_send![screen.as_ptr(), coordinateSpace]; + let rect: CGRect = msg_send![view.as_ptr(), convertRect:bounds toCoordinateSpace:screen_space]; + let scale_factor: CGFloat = msg_send![screen.as_ptr(), nativeScale]; + (rect.size, scale_factor) + } + None => (bounds.size, 1.0), + } + }, + _ => unsafe { + let render_layer_borrow = self.render_layer.lock(); + let render_layer = render_layer_borrow.as_ref(); + let bounds: CGRect = msg_send![render_layer, bounds]; + let contents_scale: CGFloat = msg_send![render_layer, contentsScale]; + (bounds.size, contents_scale) + }, + }; + + wgt::Extent3d { + width: (size.width * scale) as u32, + height: (size.height * scale) as u32, + depth_or_array_layers: 1, + } + } +} + +impl crate::Surface for super::Surface { + unsafe fn configure( + &mut self, + device: &super::Device, + config: &crate::SurfaceConfiguration, + ) -> Result<(), crate::SurfaceError> { + log::info!("build swapchain {:?}", config); + + let caps = &device.shared.private_caps; + self.raw_swapchain_format = caps.map_format(config.format); + + let render_layer = self.render_layer.lock(); + let framebuffer_only = config.usage == crate::TextureUse::COLOR_TARGET; + let display_sync = config.present_mode != wgt::PresentMode::Immediate; + let drawable_size = CGSize::new(config.extent.width as f64, config.extent.height as f64); + + match config.composite_alpha_mode { + crate::CompositeAlphaMode::Opaque => render_layer.set_opaque(true), + crate::CompositeAlphaMode::PostMultiplied => render_layer.set_opaque(false), + crate::CompositeAlphaMode::PreMultiplied => (), + } + + let device_raw = device.shared.device.lock(); + // On iOS, unless the user supplies a view with a CAMetalLayer, we + // create one as a sublayer. However, when the view changes size, + // its sublayers are not automatically resized, and we must resize + // it here. The drawable size and the layer size don't correlate + #[cfg(target_os = "ios")] + { + if let Some(view) = self.view { + let main_layer: *mut Object = msg_send![view.as_ptr(), layer]; + let bounds: CGRect = msg_send![main_layer, bounds]; + let () = msg_send![*render_layer, setFrame: bounds]; + } + } + render_layer.set_device(&*device_raw); + render_layer.set_pixel_format(self.raw_swapchain_format); + render_layer.set_framebuffer_only(framebuffer_only); + render_layer.set_presents_with_transaction(self.present_with_transaction); + + // this gets ignored on iOS for certain OS/device combinations (iphone5s iOS 10.3) + let () = msg_send![*render_layer, setMaximumDrawableCount: config.swap_chain_size as u64]; + + render_layer.set_drawable_size(drawable_size); + if caps.can_set_next_drawable_timeout { + let () = msg_send![*render_layer, setAllowsNextDrawableTimeout:false]; + } + if caps.can_set_display_sync { + let () = msg_send![*render_layer, setDisplaySyncEnabled: display_sync]; + } + + Ok(()) + } + + unsafe fn unconfigure(&mut self, _device: &super::Device) { + self.raw_swapchain_format = mtl::MTLPixelFormat::Invalid; + } + + unsafe fn acquire_texture( + &mut self, + _timeout_ms: u32, //TODO + ) -> Result>, crate::SurfaceError> { + let render_layer = self.render_layer.lock(); + let (drawable, texture) = autoreleasepool(|| { + let drawable = render_layer.next_drawable().unwrap(); + (drawable.to_owned(), drawable.texture().to_owned()) + }); + + let suf_texture = super::SurfaceTexture { + texture: super::Texture { + raw: texture, + raw_format: self.raw_swapchain_format, + raw_type: mtl::MTLTextureType::D2, + array_layers: 1, + mip_levels: 1, + }, + drawable, + present_with_transaction: self.present_with_transaction, + }; + + Ok(Some(crate::AcquiredSurfaceTexture { + texture: suf_texture, + suboptimal: false, + })) + } + + unsafe fn discard_texture(&mut self, _texture: super::SurfaceTexture) {} +} diff --git a/wgpu-hal/src/util.rs b/wgpu-hal/src/util.rs new file mode 100644 index 0000000000..0813b7a0f3 --- /dev/null +++ b/wgpu-hal/src/util.rs @@ -0,0 +1,15 @@ +pub mod db { + pub mod intel { + pub const VENDOR: u32 = 0x8086; + pub const DEVICE_KABY_LAKE_MASK: u32 = 0x5900; + pub const DEVICE_SKY_LAKE_MASK: u32 = 0x1900; + } +} + +pub fn map_naga_stage(stage: naga::ShaderStage) -> wgt::ShaderStage { + match stage { + naga::ShaderStage::Vertex => wgt::ShaderStage::VERTEX, + naga::ShaderStage::Fragment => wgt::ShaderStage::FRAGMENT, + naga::ShaderStage::Compute => wgt::ShaderStage::COMPUTE, + } +} diff --git a/wgpu-hal/src/vulkan/adapter.rs b/wgpu-hal/src/vulkan/adapter.rs new file mode 100644 index 0000000000..84faba59dd --- /dev/null +++ b/wgpu-hal/src/vulkan/adapter.rs @@ -0,0 +1,950 @@ +use super::conv; + +use ash::{ + extensions::khr, + version::{DeviceV1_0, InstanceV1_0}, + vk, +}; +use parking_lot::Mutex; + +use std::{ffi::CStr, mem, ptr, sync::Arc}; + +//TODO: const fn? +fn indexing_features() -> wgt::Features { + wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING + | wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING + | wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING +} + +/// Aggregate of the `vk::PhysicalDevice*Features` structs used by `gfx`. +#[derive(Debug, Default)] +pub struct PhysicalDeviceFeatures { + core: vk::PhysicalDeviceFeatures, + vulkan_1_2: Option, + descriptor_indexing: Option, + imageless_framebuffer: Option, +} + +// This is safe because the structs have `p_next: *mut c_void`, which we null out/never read. +unsafe impl Send for PhysicalDeviceFeatures {} +unsafe impl Sync for PhysicalDeviceFeatures {} + +impl PhysicalDeviceFeatures { + /// Add the members of `self` into `info.enabled_features` and its `p_next` chain. + fn add_to_device_create_builder<'a>( + &'a mut self, + mut info: vk::DeviceCreateInfoBuilder<'a>, + ) -> vk::DeviceCreateInfoBuilder<'a> { + info = info.enabled_features(&self.core); + if let Some(ref mut feature) = self.vulkan_1_2 { + info = info.push_next(feature); + } + if let Some(ref mut feature) = self.descriptor_indexing { + info = info.push_next(feature); + } + if let Some(ref mut feature) = self.imageless_framebuffer { + info = info.push_next(feature); + } + info + } + + /// Create a `PhysicalDeviceFeatures` that will be used to create a logical device. + /// + /// `requested_features` should be the same as what was used to generate `enabled_extensions`. + fn from_extensions_and_requested_features( + api_version: u32, + enabled_extensions: &[&'static CStr], + requested_features: wgt::Features, + downlevel_flags: wgt::DownlevelFlags, + private_caps: &super::PrivateCapabilities, + ) -> Self { + //TODO: make configurable + let rba = !(cfg!(target_os = "macos") || cfg!(target_os = "ios")); + Self { + // vk::PhysicalDeviceFeatures is a struct composed of Bool32's while + // Features is a bitfield so we need to map everything manually + core: vk::PhysicalDeviceFeatures::builder() + .robust_buffer_access(rba) + .independent_blend(true) + .sample_rate_shading(true) + .image_cube_array( + downlevel_flags.contains(wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES), + ) + //.dual_src_blend(requested_features.contains(wgt::Features::DUAL_SRC_BLENDING)) + .multi_draw_indirect( + requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT), + ) + .depth_clamp(requested_features.contains(wgt::Features::DEPTH_CLAMPING)) + .fill_mode_non_solid( + requested_features.contains(wgt::Features::NON_FILL_POLYGON_MODE), + ) + //.depth_bounds(requested_features.contains(wgt::Features::DEPTH_BOUNDS)) + //.alpha_to_one(requested_features.contains(wgt::Features::ALPHA_TO_ONE)) + //.multi_viewport(requested_features.contains(wgt::Features::MULTI_VIEWPORTS)) + .sampler_anisotropy( + downlevel_flags.contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING), + ) + .texture_compression_etc2( + requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ETC2), + ) + .texture_compression_astc_ldr( + requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR), + ) + .texture_compression_bc( + requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_BC), + ) + //.occlusion_query_precise(requested_features.contains(wgt::Features::PRECISE_OCCLUSION_QUERY)) + .pipeline_statistics_query( + requested_features.contains(wgt::Features::PIPELINE_STATISTICS_QUERY), + ) + .vertex_pipeline_stores_and_atomics( + requested_features.contains(wgt::Features::VERTEX_WRITABLE_STORAGE), + ) + .fragment_stores_and_atomics( + downlevel_flags.contains(wgt::DownlevelFlags::STORAGE_IMAGES), + ) + //.shader_image_gather_extended( + //.shader_storage_image_extended_formats( + .shader_uniform_buffer_array_dynamic_indexing( + requested_features + .contains(wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING), + ) + .shader_sampled_image_array_dynamic_indexing( + requested_features + .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING), + ) + .shader_storage_buffer_array_dynamic_indexing( + requested_features + .contains(wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING), + ) + //.shader_storage_image_array_dynamic_indexing( + //.shader_clip_distance(requested_features.contains(wgt::Features::SHADER_CLIP_DISTANCE)) + //.shader_cull_distance(requested_features.contains(wgt::Features::SHADER_CULL_DISTANCE)) + .shader_float64(requested_features.contains(wgt::Features::SHADER_FLOAT64)) + //.shader_int64(requested_features.contains(wgt::Features::SHADER_INT64)) + //.shader_int16(requested_features.contains(wgt::Features::SHADER_INT16)) + //.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY)) + .build(), + vulkan_1_2: if api_version >= vk::API_VERSION_1_2 { + Some( + vk::PhysicalDeviceVulkan12Features::builder() + //.sampler_mirror_clamp_to_edge(requested_features.contains(wgt::Features::SAMPLER_MIRROR_CLAMP_EDGE)) + .draw_indirect_count( + requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT), + ) + .descriptor_indexing(requested_features.intersects(indexing_features())) + .shader_sampled_image_array_non_uniform_indexing( + requested_features.contains( + wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ), + ) + //.shader_storage_image_array_non_uniform_indexing( + //.shader_storage_buffer_array_non_uniform_indexing( + .shader_uniform_buffer_array_non_uniform_indexing( + requested_features + .contains(wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING), + ) + .runtime_descriptor_array( + requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), + ) + //.sampler_filter_minmax(requested_features.contains(wgt::Features::SAMPLER_REDUCTION)) + .imageless_framebuffer(private_caps.imageless_framebuffers) + .build(), + ) + } else { + None + }, + descriptor_indexing: if enabled_extensions + .contains(&vk::ExtDescriptorIndexingFn::name()) + { + Some( + vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::builder() + .shader_sampled_image_array_non_uniform_indexing( + requested_features.contains( + wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + ), + ) + //.shader_storage_image_array_non_uniform_indexing( + //.shader_storage_buffer_array_non_uniform_indexing( + .shader_uniform_buffer_array_non_uniform_indexing( + requested_features + .contains(wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING), + ) + .runtime_descriptor_array( + requested_features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), + ) + .build(), + ) + } else { + None + }, + imageless_framebuffer: if enabled_extensions + .contains(&vk::KhrImagelessFramebufferFn::name()) + { + Some( + vk::PhysicalDeviceImagelessFramebufferFeaturesKHR::builder() + .imageless_framebuffer(true) + .build(), + ) + } else { + None + }, + } + } + + fn to_wgpu(&self, caps: &PhysicalDeviceCapabilities) -> (wgt::Features, wgt::DownlevelFlags) { + use wgt::{DownlevelFlags as Df, Features as F}; + let mut features = F::empty() + | F::MAPPABLE_PRIMARY_BUFFERS + | F::PUSH_CONSTANTS + | F::ADDRESS_MODE_CLAMP_TO_BORDER + | F::SAMPLED_TEXTURE_BINDING_ARRAY + | F::BUFFER_BINDING_ARRAY; + let mut dl_flags = Df::all(); + + dl_flags.set(Df::CUBE_ARRAY_TEXTURES, self.core.image_cube_array != 0); + dl_flags.set(Df::ANISOTROPIC_FILTERING, self.core.sampler_anisotropy != 0); + dl_flags.set( + Df::STORAGE_IMAGES, + self.core.fragment_stores_and_atomics != 0, + ); + + //if self.core.dual_src_blend != 0 + features.set(F::MULTI_DRAW_INDIRECT, self.core.multi_draw_indirect != 0); + features.set(F::DEPTH_CLAMPING, self.core.depth_clamp != 0); + features.set(F::NON_FILL_POLYGON_MODE, self.core.fill_mode_non_solid != 0); + //if self.core.depth_bounds != 0 { + //if self.core.alpha_to_one != 0 { + //if self.core.multi_viewport != 0 { + features.set( + F::TEXTURE_COMPRESSION_ETC2, + self.core.texture_compression_etc2 != 0, + ); + features.set( + F::TEXTURE_COMPRESSION_ASTC_LDR, + self.core.texture_compression_astc_ldr != 0, + ); + features.set( + F::TEXTURE_COMPRESSION_BC, + self.core.texture_compression_bc != 0, + ); + //if self.core.occlusion_query_precise != 0 { + //if self.core.pipeline_statistics_query != 0 { //TODO + features.set( + F::VERTEX_WRITABLE_STORAGE, + self.core.vertex_pipeline_stores_and_atomics != 0, + ); + //if self.core.shader_image_gather_extended != 0 { + //if self.core.shader_storage_image_extended_formats != 0 { + features.set( + F::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, + self.core.shader_uniform_buffer_array_dynamic_indexing != 0, + ); + features.set( + F::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, + self.core.shader_sampled_image_array_dynamic_indexing != 0, + ); + features.set( + F::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, + self.core.shader_storage_buffer_array_dynamic_indexing != 0, + ); + //if self.core.shader_storage_image_array_dynamic_indexing != 0 { + //if self.core.shader_clip_distance != 0 { + //if self.core.shader_cull_distance != 0 { + features.set(F::SHADER_FLOAT64, self.core.shader_float64 != 0); + //if self.core.shader_int64 != 0 { + //if self.core.shader_int16 != 0 { + + //if caps.supports_extension(vk::KhrSamplerMirrorClampToEdgeFn::name()) { + //if caps.supports_extension(vk::ExtSamplerFilterMinmaxFn::name()) { + features.set( + F::MULTI_DRAW_INDIRECT_COUNT, + caps.supports_extension(khr::DrawIndirectCount::name()), + ); + features.set( + F::CONSERVATIVE_RASTERIZATION, + caps.supports_extension(vk::ExtConservativeRasterizationFn::name()), + ); + + if let Some(ref vulkan_1_2) = self.vulkan_1_2 { + if vulkan_1_2.shader_sampled_image_array_non_uniform_indexing != 0 { + features |= F::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING; + } + //if vulkan_1_2.shader_storage_image_array_non_uniform_indexing != 0 { + //if vulkan_1_2.shader_storage_buffer_array_non_uniform_indexing != 0 { + if vulkan_1_2.shader_uniform_buffer_array_non_uniform_indexing != 0 { + features |= F::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING; + } + if vulkan_1_2.runtime_descriptor_array != 0 { + features |= F::UNSIZED_BINDING_ARRAY; + } + //if vulkan_1_2.sampler_mirror_clamp_to_edge != 0 { + //if vulkan_1_2.sampler_filter_minmax != 0 { + if vulkan_1_2.draw_indirect_count != 0 { + features |= F::MULTI_DRAW_INDIRECT_COUNT; + } + } + + if let Some(ref descriptor_indexing) = self.descriptor_indexing { + if descriptor_indexing.shader_sampled_image_array_non_uniform_indexing != 0 { + features |= F::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING; + } + //if descriptor_indexing.shader_storage_image_array_non_uniform_indexing != 0 { + //if descriptor_indexing.shader_storage_buffer_array_non_uniform_indexing != 0 { + if descriptor_indexing.shader_uniform_buffer_array_non_uniform_indexing != 0 { + features |= F::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING; + } + if descriptor_indexing.runtime_descriptor_array != 0 { + features |= F::UNSIZED_BINDING_ARRAY; + } + } + + (features, dl_flags) + } +} + +/// Information gathered about a physical device capabilities. +pub struct PhysicalDeviceCapabilities { + supported_extensions: Vec, + properties: vk::PhysicalDeviceProperties, +} + +impl PhysicalDeviceCapabilities { + fn supports_extension(&self, extension: &CStr) -> bool { + self.supported_extensions + .iter() + .any(|ep| unsafe { CStr::from_ptr(ep.extension_name.as_ptr()) } == extension) + } + + /// Map `requested_features` to the list of Vulkan extension strings required to create the logical device. + fn get_required_extensions(&self, requested_features: wgt::Features) -> Vec<&'static CStr> { + let mut extensions = Vec::new(); + + extensions.push(khr::Swapchain::name()); + + if self.properties.api_version < vk::API_VERSION_1_1 { + extensions.push(vk::KhrMaintenance1Fn::name()); + extensions.push(vk::KhrMaintenance2Fn::name()); + + // `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside `VK_KHR_maintenance1` or a 1.1+ device. + if !self.supports_extension(vk::KhrMaintenance1Fn::name()) { + extensions.push(vk::AmdNegativeViewportHeightFn::name()); + } + } + + if self.properties.api_version < vk::API_VERSION_1_2 { + if self.supports_extension(vk::KhrImagelessFramebufferFn::name()) { + extensions.push(vk::KhrImagelessFramebufferFn::name()); + extensions.push(vk::KhrImageFormatListFn::name()); // Required for `KhrImagelessFramebufferFn` + } + + extensions.push(vk::ExtSamplerFilterMinmaxFn::name()); + + if requested_features.intersects(indexing_features()) { + extensions.push(vk::ExtDescriptorIndexingFn::name()); + + if self.properties.api_version < vk::API_VERSION_1_1 { + extensions.push(vk::KhrMaintenance3Fn::name()); + } + } + + //extensions.push(vk::KhrSamplerMirrorClampToEdgeFn::name()); + //extensions.push(vk::ExtSamplerFilterMinmaxFn::name()); + + if requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT) { + extensions.push(khr::DrawIndirectCount::name()); + } + } + + if requested_features.contains(wgt::Features::CONSERVATIVE_RASTERIZATION) { + extensions.push(vk::ExtConservativeRasterizationFn::name()); + } + + extensions + } + + fn to_wgpu_limits(&self) -> wgt::Limits { + let limits = &self.properties.limits; + wgt::Limits { + max_texture_dimension_1d: limits.max_image_dimension1_d, + max_texture_dimension_2d: limits.max_image_dimension2_d, + max_texture_dimension_3d: limits.max_image_dimension3_d, + max_texture_array_layers: limits.max_image_array_layers, + max_bind_groups: limits + .max_bound_descriptor_sets + .min(crate::MAX_BIND_GROUPS as u32), + max_dynamic_uniform_buffers_per_pipeline_layout: limits + .max_descriptor_set_uniform_buffers_dynamic, + max_dynamic_storage_buffers_per_pipeline_layout: limits + .max_descriptor_set_storage_buffers_dynamic, + max_sampled_textures_per_shader_stage: limits.max_per_stage_descriptor_sampled_images, + max_samplers_per_shader_stage: limits.max_per_stage_descriptor_samplers, + max_storage_buffers_per_shader_stage: limits.max_per_stage_descriptor_storage_buffers, + max_storage_textures_per_shader_stage: limits.max_per_stage_descriptor_storage_images, + max_uniform_buffers_per_shader_stage: limits.max_per_stage_descriptor_uniform_buffers, + max_uniform_buffer_binding_size: limits.max_uniform_buffer_range, + max_storage_buffer_binding_size: limits.max_storage_buffer_range, + max_vertex_buffers: limits + .max_vertex_input_bindings + .min(crate::MAX_VERTEX_BUFFERS as u32), + max_vertex_attributes: limits.max_vertex_input_attributes, + max_vertex_buffer_array_stride: limits.max_vertex_input_binding_stride, + max_push_constant_size: limits.max_push_constants_size, + } + } + + fn to_hal_alignments(&self) -> crate::Alignments { + let limits = &self.properties.limits; + crate::Alignments { + buffer_copy_offset: wgt::BufferSize::new(limits.optimal_buffer_copy_offset_alignment) + .unwrap(), + buffer_copy_pitch: wgt::BufferSize::new(limits.optimal_buffer_copy_row_pitch_alignment) + .unwrap(), + storage_buffer_offset: wgt::BufferSize::new(limits.min_storage_buffer_offset_alignment) + .unwrap(), + uniform_buffer_offset: wgt::BufferSize::new(limits.min_uniform_buffer_offset_alignment) + .unwrap(), + } + } +} + +impl super::InstanceShared { + #[allow(trivial_casts)] // false positives + fn inspect( + &self, + phd: vk::PhysicalDevice, + ) -> (PhysicalDeviceCapabilities, PhysicalDeviceFeatures) { + let capabilities = unsafe { + PhysicalDeviceCapabilities { + supported_extensions: self.raw.enumerate_device_extension_properties(phd).unwrap(), + properties: self.raw.get_physical_device_properties(phd), + } + }; + + let mut features = PhysicalDeviceFeatures::default(); + features.core = if let Some(ref get_device_properties) = self.get_physical_device_properties + { + let core = vk::PhysicalDeviceFeatures::builder().build(); + let mut features2 = vk::PhysicalDeviceFeatures2KHR::builder() + .features(core) + .build(); + + if capabilities.properties.api_version >= vk::API_VERSION_1_2 { + features.vulkan_1_2 = Some(vk::PhysicalDeviceVulkan12Features::builder().build()); + + let mut_ref = features.vulkan_1_2.as_mut().unwrap(); + mut_ref.p_next = mem::replace(&mut features2.p_next, mut_ref as *mut _ as *mut _); + } + + if capabilities.supports_extension(vk::ExtDescriptorIndexingFn::name()) { + features.descriptor_indexing = + Some(vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::builder().build()); + + let mut_ref = features.descriptor_indexing.as_mut().unwrap(); + mut_ref.p_next = mem::replace(&mut features2.p_next, mut_ref as *mut _ as *mut _); + } + + // `VK_KHR_imageless_framebuffer` is promoted to 1.2, but has no changes, so we can keep using the extension unconditionally. + if capabilities.supports_extension(vk::KhrImagelessFramebufferFn::name()) { + features.imageless_framebuffer = + Some(vk::PhysicalDeviceImagelessFramebufferFeaturesKHR::builder().build()); + + let mut_ref = features.imageless_framebuffer.as_mut().unwrap(); + mut_ref.p_next = mem::replace(&mut features2.p_next, mut_ref as *mut _ as *mut _); + } + + unsafe { + get_device_properties.get_physical_device_features2_khr(phd, &mut features2); + } + features2.features + } else { + unsafe { self.raw.get_physical_device_features(phd) } + }; + + /// # Safety + /// `T` must be a struct bigger than `vk::BaseOutStructure`. + unsafe fn null_p_next(features: &mut Option) { + if let Some(ref mut features) = *features { + // This is technically invalid since `vk::BaseOutStructure` and `T` will probably never have the same size. + (*(features as *mut T as *mut vk::BaseOutStructure)).p_next = ptr::null_mut(); + } + } + + unsafe { + null_p_next(&mut features.vulkan_1_2); + null_p_next(&mut features.descriptor_indexing); + null_p_next(&mut features.imageless_framebuffer); + } + + (capabilities, features) + } +} + +impl super::Instance { + pub(super) fn expose_adapter( + &self, + phd: vk::PhysicalDevice, + ) -> Option> { + let (phd_capabilities, phd_features) = self.shared.inspect(phd); + + let info = wgt::AdapterInfo { + name: unsafe { + CStr::from_ptr(phd_capabilities.properties.device_name.as_ptr()) + .to_str() + .unwrap_or("?") + .to_owned() + }, + vendor: phd_capabilities.properties.vendor_id as usize, + device: phd_capabilities.properties.device_id as usize, + device_type: match phd_capabilities.properties.device_type { + ash::vk::PhysicalDeviceType::OTHER => wgt::DeviceType::Other, + ash::vk::PhysicalDeviceType::INTEGRATED_GPU => wgt::DeviceType::IntegratedGpu, + ash::vk::PhysicalDeviceType::DISCRETE_GPU => wgt::DeviceType::DiscreteGpu, + ash::vk::PhysicalDeviceType::VIRTUAL_GPU => wgt::DeviceType::VirtualGpu, + ash::vk::PhysicalDeviceType::CPU => wgt::DeviceType::Cpu, + _ => wgt::DeviceType::Other, + }, + backend: wgt::Backend::Vulkan, + }; + + let (available_features, downlevel_flags) = phd_features.to_wgpu(&phd_capabilities); + { + use crate::util::db; + // see https://github.com/gfx-rs/gfx/issues/1930 + let _is_windows_intel_dual_src_bug = cfg!(windows) + && phd_capabilities.properties.vendor_id == db::intel::VENDOR + && (phd_capabilities.properties.device_id & db::intel::DEVICE_KABY_LAKE_MASK + == db::intel::DEVICE_KABY_LAKE_MASK + || phd_capabilities.properties.device_id & db::intel::DEVICE_SKY_LAKE_MASK + == db::intel::DEVICE_SKY_LAKE_MASK); + }; + + if phd_features.core.sample_rate_shading == 0 { + log::error!( + "sample_rate_shading feature is not supported, hiding the adapter: {}", + info.name + ); + return None; + } + if !phd_capabilities.supports_extension(vk::AmdNegativeViewportHeightFn::name()) + && !phd_capabilities.supports_extension(vk::KhrMaintenance1Fn::name()) + && phd_capabilities.properties.api_version < vk::API_VERSION_1_2 + { + log::error!( + "viewport Y-flip is not supported, hiding the adapter: {}", + info.name + ); + return None; + } + + let queue_families = unsafe { + self.shared + .raw + .get_physical_device_queue_family_properties(phd) + }; + let queue_flags = queue_families.first()?.queue_flags; + if !queue_flags.contains(vk::QueueFlags::GRAPHICS) { + log::warn!("The first queue only exposes {:?}", queue_flags); + return None; + } + + let private_caps = super::PrivateCapabilities { + flip_y_requires_shift: phd_capabilities.properties.api_version >= vk::API_VERSION_1_1 + || phd_capabilities.supports_extension(vk::KhrMaintenance1Fn::name()), + imageless_framebuffers: phd_features + .vulkan_1_2 + .map_or(false, |features| features.imageless_framebuffer == vk::TRUE) + || phd_capabilities.supports_extension(vk::KhrImagelessFramebufferFn::name()), + image_view_usage: phd_capabilities.properties.api_version >= vk::API_VERSION_1_1 + || phd_capabilities.supports_extension(vk::KhrMaintenance2Fn::name()), + texture_d24: unsafe { + self.shared + .raw + .get_physical_device_format_properties(phd, vk::Format::X8_D24_UNORM_PACK32) + .optimal_tiling_features + .contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT) + }, + texture_d24_s8: unsafe { + self.shared + .raw + .get_physical_device_format_properties(phd, vk::Format::D24_UNORM_S8_UINT) + .optimal_tiling_features + .contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT) + }, + non_coherent_map_mask: phd_capabilities.properties.limits.non_coherent_atom_size - 1, + }; + + let capabilities = crate::Capabilities { + limits: phd_capabilities.to_wgpu_limits(), + alignments: phd_capabilities.to_hal_alignments(), + downlevel: wgt::DownlevelCapabilities { + flags: downlevel_flags, + shader_model: wgt::ShaderModel::Sm5, //TODO? + }, + }; + + let adapter = super::Adapter { + raw: phd, + instance: Arc::clone(&self.shared), + //queue_families, + known_memory_flags: vk::MemoryPropertyFlags::DEVICE_LOCAL + | vk::MemoryPropertyFlags::HOST_VISIBLE + | vk::MemoryPropertyFlags::HOST_COHERENT + | vk::MemoryPropertyFlags::HOST_CACHED + | vk::MemoryPropertyFlags::LAZILY_ALLOCATED, + phd_capabilities, + //phd_features, + downlevel_flags, + private_caps, + }; + + Some(crate::ExposedAdapter { + adapter, + info, + features: available_features, + capabilities, + }) + } +} + +impl crate::Adapter for super::Adapter { + unsafe fn open( + &self, + features: wgt::Features, + ) -> Result, crate::DeviceError> { + let enabled_extensions = { + let (supported_extensions, unsupported_extensions) = self + .phd_capabilities + .get_required_extensions(features) + .iter() + .partition::, _>(|&&extension| { + self.phd_capabilities.supports_extension(extension) + }); + + if !unsupported_extensions.is_empty() { + log::warn!("Missing extensions: {:?}", unsupported_extensions); + } + + log::debug!("Supported extensions: {:?}", supported_extensions); + supported_extensions + }; + + let mem_properties = self + .instance + .raw + .get_physical_device_memory_properties(self.raw); + let memory_types = + &mem_properties.memory_types[..mem_properties.memory_type_count as usize]; + let valid_ash_memory_types = memory_types.iter().enumerate().fold(0, |u, (i, mem)| { + if self.known_memory_flags.contains(mem.property_flags) { + u | (1 << i) + } else { + u + } + }); + + // Create device + let family_index = 0; //TODO + let raw_device = { + let family_info = vk::DeviceQueueCreateInfo::builder() + .queue_family_index(family_index) + .queue_priorities(&[1.0]) + .build(); + let family_infos = [family_info]; + + let str_pointers = enabled_extensions + .iter() + .map(|&s| { + // Safe because `enabled_extensions` entries have static lifetime. + s.as_ptr() + }) + .collect::>(); + + let mut enabled_phd_features = + PhysicalDeviceFeatures::from_extensions_and_requested_features( + self.phd_capabilities.properties.api_version, + &enabled_extensions, + features, + self.downlevel_flags, + &self.private_caps, + ); + let pre_info = vk::DeviceCreateInfo::builder() + .queue_create_infos(&family_infos) + .enabled_extension_names(&str_pointers); + let info = enabled_phd_features.add_to_device_create_builder(pre_info); + + self.instance.raw.create_device(self.raw, &info, None)? + }; + + let swapchain_fn = khr::Swapchain::new(&self.instance.raw, &raw_device); + + let indirect_count_fn = if enabled_extensions.contains(&khr::DrawIndirectCount::name()) { + Some(super::ExtensionFn::Extension(khr::DrawIndirectCount::new( + &self.instance.raw, + &raw_device, + ))) + } else if self.phd_capabilities.properties.api_version >= vk::API_VERSION_1_2 { + Some(super::ExtensionFn::Promoted) + } else { + None + }; + + let naga_options = { + use naga::back::spv; + let capabilities = [ + spv::Capability::Shader, + spv::Capability::Matrix, + spv::Capability::Sampled1D, + spv::Capability::Image1D, + spv::Capability::ImageQuery, + spv::Capability::DerivativeControl, + //TODO: fill out the rest + ]; + let mut flags = spv::WriterFlags::empty(); + flags.set( + spv::WriterFlags::DEBUG, + self.instance.flags.contains(crate::InstanceFlag::DEBUG), + ); + spv::Options { + lang_version: (1, 0), + flags, + capabilities: Some(capabilities.iter().cloned().collect()), + } + }; + + log::info!("Private capabilities: {:?}", self.private_caps); + let raw_queue = raw_device.get_device_queue(family_index, 0); + + let shared = Arc::new(super::DeviceShared { + raw: raw_device, + instance: Arc::clone(&self.instance), + extension_fns: super::DeviceExtensionFunctions { + draw_indirect_count: indirect_count_fn, + }, + vendor_id: self.phd_capabilities.properties.vendor_id, + downlevel_flags: self.downlevel_flags, + private_caps: self.private_caps.clone(), + _timestamp_period: self.phd_capabilities.properties.limits.timestamp_period, + render_passes: Mutex::new(Default::default()), + framebuffers: Mutex::new(Default::default()), + }); + let queue = super::Queue { + raw: raw_queue, + swapchain_fn, + device: Arc::clone(&shared), + family_index, + }; + + let mem_allocator = { + let limits = self.phd_capabilities.properties.limits; + let config = gpu_alloc::Config::i_am_prototyping(); //TODO + let properties = gpu_alloc::DeviceProperties { + max_memory_allocation_count: limits.max_memory_allocation_count, + max_memory_allocation_size: u64::max_value(), // TODO + non_coherent_atom_size: limits.non_coherent_atom_size, + memory_types: memory_types + .iter() + .map(|memory_type| gpu_alloc::MemoryType { + props: gpu_alloc::MemoryPropertyFlags::from_bits_truncate( + memory_type.property_flags.as_raw() as u8, + ), + heap: memory_type.heap_index, + }) + .collect(), + memory_heaps: mem_properties.memory_heaps + [..mem_properties.memory_heap_count as usize] + .iter() + .map(|&memory_heap| gpu_alloc::MemoryHeap { + size: memory_heap.size, + }) + .collect(), + buffer_device_address: false, + }; + gpu_alloc::GpuAllocator::new(config, properties) + }; + let desc_allocator = gpu_descriptor::DescriptorAllocator::new(0); + + let device = super::Device { + shared, + mem_allocator: Mutex::new(mem_allocator), + desc_allocator: Mutex::new(desc_allocator), + valid_ash_memory_types, + naga_options, + }; + + Ok(crate::OpenDevice { device, queue }) + } + + unsafe fn texture_format_capabilities( + &self, + format: wgt::TextureFormat, + ) -> crate::TextureFormatCapability { + use crate::TextureFormatCapability as Tfc; + let vk_format = self.private_caps.map_texture_format(format); + let properties = self + .instance + .raw + .get_physical_device_format_properties(self.raw, vk_format); + let features = properties.linear_tiling_features; + + let mut flags = Tfc::empty(); + flags.set( + Tfc::SAMPLED, + features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE), + ); + flags.set( + Tfc::SAMPLED_LINEAR, + features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR), + ); + flags.set( + Tfc::SAMPLED_MINMAX, + features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_MINMAX), + ); + flags.set( + Tfc::STORAGE | Tfc::STORAGE_READ_WRITE, + features.contains(vk::FormatFeatureFlags::STORAGE_IMAGE), + ); + flags.set( + Tfc::STORAGE_ATOMIC, + features.contains(vk::FormatFeatureFlags::STORAGE_IMAGE_ATOMIC), + ); + flags.set( + Tfc::COLOR_ATTACHMENT, + features.contains(vk::FormatFeatureFlags::COLOR_ATTACHMENT), + ); + flags.set( + Tfc::COLOR_ATTACHMENT_BLEND, + features.contains(vk::FormatFeatureFlags::COLOR_ATTACHMENT_BLEND), + ); + flags.set( + Tfc::DEPTH_STENCIL_ATTACHMENT, + features.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT), + ); + flags.set( + Tfc::COPY_SRC, + features.intersects( + vk::FormatFeatureFlags::TRANSFER_SRC | vk::FormatFeatureFlags::BLIT_SRC, + ), + ); + flags.set( + Tfc::COPY_DST, + features.intersects( + vk::FormatFeatureFlags::TRANSFER_DST | vk::FormatFeatureFlags::BLIT_DST, + ), + ); + flags + } + + unsafe fn surface_capabilities( + &self, + surface: &super::Surface, + ) -> Option { + let queue_family_index = 0; //TODO + match surface.functor.get_physical_device_surface_support( + self.raw, + queue_family_index, + surface.raw, + ) { + Ok(true) => (), + Ok(false) => return None, + Err(e) => { + log::error!("get_physical_device_surface_support: {}", e); + return None; + } + } + + let caps = match surface + .functor + .get_physical_device_surface_capabilities(self.raw, surface.raw) + { + Ok(caps) => caps, + Err(e) => { + log::error!("get_physical_device_surface_capabilities: {}", e); + return None; + } + }; + + // If image count is 0, the support number of images is unlimited. + let max_image_count = if caps.max_image_count == 0 { + !0 + } else { + caps.max_image_count + }; + + // `0xFFFFFFFF` indicates that the extent depends on the created swapchain. + let current_extent = if caps.current_extent.width != !0 && caps.current_extent.height != !0 + { + Some(wgt::Extent3d { + width: caps.current_extent.width, + height: caps.current_extent.height, + depth_or_array_layers: 1, + }) + } else { + None + }; + + let min_extent = wgt::Extent3d { + width: caps.min_image_extent.width, + height: caps.min_image_extent.height, + depth_or_array_layers: 1, + }; + + let max_extent = wgt::Extent3d { + width: caps.max_image_extent.width, + height: caps.max_image_extent.height, + depth_or_array_layers: caps.max_image_array_layers, + }; + + let raw_present_modes = match surface + .functor + .get_physical_device_surface_present_modes(self.raw, surface.raw) + { + Ok(present_modes) => present_modes, + Err(e) => { + log::error!("get_physical_device_surface_present_modes: {}", e); + Vec::new() + } + }; + + let raw_surface_formats = match surface + .functor + .get_physical_device_surface_formats(self.raw, surface.raw) + { + Ok(formats) => formats, + Err(e) => { + log::error!("get_physical_device_surface_formats: {}", e); + Vec::new() + } + }; + + let supported_formats = [ + wgt::TextureFormat::Rgba8Unorm, + wgt::TextureFormat::Rgba8UnormSrgb, + wgt::TextureFormat::Bgra8Unorm, + wgt::TextureFormat::Bgra8UnormSrgb, + ]; + let formats = supported_formats + .iter() + .cloned() + .filter(|&format| { + let vk_format = self.private_caps.map_texture_format(format); + raw_surface_formats + .iter() + .any(|sf| sf.format == vk_format || sf.format == vk::Format::UNDEFINED) + }) + .collect(); + + Some(crate::SurfaceCapabilities { + formats, + swap_chain_sizes: 1..=max_image_count, + current_extent, + extents: min_extent..=max_extent, + usage: conv::map_vk_image_usage(caps.supported_usage_flags), + present_modes: raw_present_modes + .into_iter() + .flat_map(conv::map_vk_present_mode) + .collect(), + composite_alpha_modes: conv::map_vk_composite_alpha(caps.supported_composite_alpha), + }) + } +} diff --git a/wgpu-hal/src/vulkan/command.rs b/wgpu-hal/src/vulkan/command.rs new file mode 100644 index 0000000000..1f2894f0b3 --- /dev/null +++ b/wgpu-hal/src/vulkan/command.rs @@ -0,0 +1,747 @@ +use super::conv; + +use arrayvec::ArrayVec; +use ash::{ + extensions::ext, + version::{DeviceV1_0, DeviceV1_2}, + vk, +}; +use inplace_it::inplace_or_alloc_from_iter; + +use std::{mem, ops::Range, slice}; + +const ALLOCATION_GRANULARITY: u32 = 16; +const DST_IMAGE_LAYOUT: vk::ImageLayout = vk::ImageLayout::TRANSFER_DST_OPTIMAL; + +impl super::Texture { + fn map_buffer_copies(&self, regions: T) -> impl Iterator + where + T: Iterator, + { + let dim = self.dim; + let aspects = self.aspects; + let fi = self.format_info; + regions.map(move |r| { + let (layer_count, image_extent) = conv::map_extent(r.size, dim); + let (image_subresource, image_offset) = + conv::map_subresource_layers(&r.texture_base, dim, aspects, layer_count); + vk::BufferImageCopy { + buffer_offset: r.buffer_layout.offset, + buffer_row_length: r.buffer_layout.bytes_per_row.map_or(0, |bpr| { + fi.block_dimensions.0 as u32 * (bpr.get() / fi.block_size as u32) + }), + buffer_image_height: r + .buffer_layout + .rows_per_image + .map_or(0, |rpi| rpi.get() * fi.block_dimensions.1 as u32), + image_subresource, + image_offset, + image_extent, + } + }) + } +} + +impl super::DeviceShared { + fn debug_messenger(&self) -> Option<&ext::DebugUtils> { + Some(&self.instance.debug_utils.as_ref()?.extension) + } +} + +impl crate::CommandEncoder for super::CommandEncoder { + unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> { + if self.free.is_empty() { + let vk_info = vk::CommandBufferAllocateInfo::builder() + .command_pool(self.raw) + .command_buffer_count(ALLOCATION_GRANULARITY) + .build(); + let cmd_buf_vec = self.device.raw.allocate_command_buffers(&vk_info)?; + self.free.extend(cmd_buf_vec); + } + let raw = self.free.pop().unwrap(); + + // Set the name unconditionally, since there might be a + // previous name assigned to this. + self.device.set_object_name( + vk::ObjectType::COMMAND_BUFFER, + raw, + label.unwrap_or_default(), + ); + + let vk_info = vk::CommandBufferBeginInfo::builder() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT) + .build(); + self.device.raw.begin_command_buffer(raw, &vk_info)?; + self.active = raw; + + Ok(()) + } + + unsafe fn end_encoding(&mut self) -> Result { + let raw = self.active; + self.active = vk::CommandBuffer::null(); + self.device.raw.end_command_buffer(raw)?; + Ok(super::CommandBuffer { raw }) + } + + unsafe fn discard_encoding(&mut self) { + self.discarded.push(self.active); + self.active = vk::CommandBuffer::null(); + } + + unsafe fn reset_all(&mut self, cmd_bufs: I) + where + I: Iterator, + { + self.temp.clear(); + self.free + .extend(cmd_bufs.into_iter().map(|cmd_buf| cmd_buf.raw)); + self.free.extend(self.discarded.drain(..)); + let _ = self + .device + .raw + .reset_command_pool(self.raw, vk::CommandPoolResetFlags::RELEASE_RESOURCES); + } + + unsafe fn transition_buffers<'a, T>(&mut self, barriers: T) + where + T: Iterator>, + { + //Note: this is done so that we never end up with empty stage flags + let mut src_stages = vk::PipelineStageFlags::TOP_OF_PIPE; + let mut dst_stages = vk::PipelineStageFlags::BOTTOM_OF_PIPE; + let vk_barriers = &mut self.temp.buffer_barriers; + vk_barriers.clear(); + + for bar in barriers { + let (src_stage, src_access) = conv::map_buffer_usage_to_barrier(bar.usage.start); + src_stages |= src_stage; + let (dst_stage, dst_access) = conv::map_buffer_usage_to_barrier(bar.usage.end); + dst_stages |= dst_stage; + + vk_barriers.push( + vk::BufferMemoryBarrier::builder() + .buffer(bar.buffer.raw) + .size(vk::WHOLE_SIZE) + .src_access_mask(src_access) + .dst_access_mask(dst_access) + .build(), + ) + } + + if !vk_barriers.is_empty() { + self.device.raw.cmd_pipeline_barrier( + self.active, + src_stages, + dst_stages, + vk::DependencyFlags::empty(), + &[], + vk_barriers, + &[], + ); + } + } + + unsafe fn transition_textures<'a, T>(&mut self, barriers: T) + where + T: Iterator>, + { + let mut src_stages = vk::PipelineStageFlags::empty(); + let mut dst_stages = vk::PipelineStageFlags::empty(); + let vk_barriers = &mut self.temp.image_barriers; + vk_barriers.clear(); + + for bar in barriers { + let range = conv::map_subresource_range(&bar.range, bar.texture.aspects); + let (src_stage, src_access) = conv::map_texture_usage_to_barrier(bar.usage.start); + let src_layout = conv::derive_image_layout(bar.usage.start, bar.texture.aspects); + src_stages |= src_stage; + let (dst_stage, dst_access) = conv::map_texture_usage_to_barrier(bar.usage.end); + let dst_layout = conv::derive_image_layout(bar.usage.end, bar.texture.aspects); + dst_stages |= dst_stage; + + vk_barriers.push( + vk::ImageMemoryBarrier::builder() + .image(bar.texture.raw) + .subresource_range(range) + .src_access_mask(src_access) + .dst_access_mask(dst_access) + .old_layout(src_layout) + .new_layout(dst_layout) + .build(), + ); + } + + if !vk_barriers.is_empty() { + self.device.raw.cmd_pipeline_barrier( + self.active, + src_stages, + dst_stages, + vk::DependencyFlags::empty(), + &[], + &[], + vk_barriers, + ); + } + } + + unsafe fn fill_buffer(&mut self, buffer: &super::Buffer, range: crate::MemoryRange, value: u8) { + self.device.raw.cmd_fill_buffer( + self.active, + buffer.raw, + range.start, + range.end - range.start, + (value as u32) * 0x01010101, + ); + } + + unsafe fn copy_buffer_to_buffer( + &mut self, + src: &super::Buffer, + dst: &super::Buffer, + regions: T, + ) where + T: Iterator, + { + let vk_regions_iter = regions.map(|r| vk::BufferCopy { + src_offset: r.src_offset, + dst_offset: r.dst_offset, + size: r.size.get(), + }); + + inplace_or_alloc_from_iter(vk_regions_iter, |vk_regions| { + self.device + .raw + .cmd_copy_buffer(self.active, src.raw, dst.raw, vk_regions) + }) + } + + unsafe fn copy_texture_to_texture( + &mut self, + src: &super::Texture, + src_usage: crate::TextureUse, + dst: &super::Texture, + regions: T, + ) where + T: Iterator, + { + let src_layout = conv::derive_image_layout(src_usage, src.aspects); + + let vk_regions_iter = regions.map(|r| { + let (layer_count, extent) = conv::map_extent(r.size, src.dim); + let (src_subresource, src_offset) = + conv::map_subresource_layers(&r.src_base, src.dim, src.aspects, layer_count); + let (dst_subresource, dst_offset) = + conv::map_subresource_layers(&r.dst_base, dst.dim, dst.aspects, layer_count); + vk::ImageCopy { + src_subresource, + src_offset, + dst_subresource, + dst_offset, + extent, + } + }); + + inplace_or_alloc_from_iter(vk_regions_iter, |vk_regions| { + self.device.raw.cmd_copy_image( + self.active, + src.raw, + src_layout, + dst.raw, + DST_IMAGE_LAYOUT, + vk_regions, + ); + }); + } + + unsafe fn copy_buffer_to_texture( + &mut self, + src: &super::Buffer, + dst: &super::Texture, + regions: T, + ) where + T: Iterator, + { + let vk_regions_iter = dst.map_buffer_copies(regions); + + inplace_or_alloc_from_iter(vk_regions_iter, |vk_regions| { + self.device.raw.cmd_copy_buffer_to_image( + self.active, + src.raw, + dst.raw, + DST_IMAGE_LAYOUT, + vk_regions, + ); + }); + } + + unsafe fn copy_texture_to_buffer( + &mut self, + src: &super::Texture, + src_usage: crate::TextureUse, + dst: &super::Buffer, + regions: T, + ) where + T: Iterator, + { + let src_layout = conv::derive_image_layout(src_usage, src.aspects); + let vk_regions_iter = src.map_buffer_copies(regions); + + inplace_or_alloc_from_iter(vk_regions_iter, |vk_regions| { + self.device.raw.cmd_copy_image_to_buffer( + self.active, + src.raw, + src_layout, + dst.raw, + vk_regions, + ); + }); + } + + unsafe fn begin_query(&mut self, set: &super::QuerySet, index: u32) { + self.device.raw.cmd_begin_query( + self.active, + set.raw, + index, + vk::QueryControlFlags::empty(), + ); + } + unsafe fn end_query(&mut self, set: &super::QuerySet, index: u32) { + self.device.raw.cmd_end_query(self.active, set.raw, index); + } + unsafe fn write_timestamp(&mut self, set: &super::QuerySet, index: u32) { + self.device.raw.cmd_write_timestamp( + self.active, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + set.raw, + index, + ); + } + unsafe fn reset_queries(&mut self, set: &super::QuerySet, range: Range) { + self.device.raw.cmd_reset_query_pool( + self.active, + set.raw, + range.start, + range.end - range.start, + ); + } + unsafe fn copy_query_results( + &mut self, + set: &super::QuerySet, + range: Range, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + ) { + self.device.raw.cmd_copy_query_pool_results( + self.active, + set.raw, + range.start, + range.end - range.start, + buffer.raw, + offset, + 0, + vk::QueryResultFlags::TYPE_64 | vk::QueryResultFlags::WAIT, + ); + } + + // render + + unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor) { + let mut vk_clear_values = ArrayVec::<[vk::ClearValue; super::MAX_TOTAL_ATTACHMENTS]>::new(); + let mut vk_image_views = ArrayVec::<[vk::ImageView; super::MAX_TOTAL_ATTACHMENTS]>::new(); + let mut rp_key = super::RenderPassKey::default(); + let mut fb_key = super::FramebufferKey::default(); + let caps = &self.device.private_caps; + + for cat in desc.color_attachments { + vk_clear_values.push(vk::ClearValue { + color: cat.make_vk_clear_color(), + }); + vk_image_views.push(cat.target.view.raw); + rp_key.colors.push(super::ColorAttachmentKey { + base: cat.target.make_attachment_key(cat.ops, caps), + resolve: cat + .resolve_target + .as_ref() + .map(|target| target.make_attachment_key(crate::AttachmentOp::STORE, caps)), + }); + fb_key.add(cat.target.view); + if let Some(ref at) = cat.resolve_target { + vk_clear_values.push(mem::zeroed()); + vk_image_views.push(at.view.raw); + fb_key.add(at.view); + } + } + if let Some(ref ds) = desc.depth_stencil_attachment { + vk_clear_values.push(vk::ClearValue { + depth_stencil: vk::ClearDepthStencilValue { + depth: ds.clear_value.0, + stencil: ds.clear_value.1, + }, + }); + vk_image_views.push(ds.target.view.raw); + rp_key.depth_stencil = Some(super::DepthStencilAttachmentKey { + base: ds.target.make_attachment_key(ds.depth_ops, caps), + stencil_ops: ds.stencil_ops, + }); + fb_key.add(ds.target.view); + } + rp_key.sample_count = fb_key.sample_count; + + let render_area = vk::Rect2D { + offset: vk::Offset2D { x: 0, y: 0 }, + extent: vk::Extent2D { + width: fb_key.extent.width, + height: fb_key.extent.height, + }, + }; + let vk_viewports = [vk::Viewport { + x: 0.0, + y: if self.device.private_caps.flip_y_requires_shift { + fb_key.extent.height as f32 + } else { + 0.0 + }, + width: fb_key.extent.width as f32, + height: -(fb_key.extent.height as f32), + min_depth: 0.0, + max_depth: 1.0, + }]; + let vk_scissors = [render_area]; + + let raw_pass = self.device.make_render_pass(rp_key).unwrap(); + + let raw_framebuffer = self + .device + .make_framebuffer(fb_key, raw_pass, desc.label) + .unwrap(); + + let mut vk_attachment_info = vk::RenderPassAttachmentBeginInfo::builder() + .attachments(&vk_image_views) + .build(); + let mut vk_info = vk::RenderPassBeginInfo::builder() + .render_pass(raw_pass) + .render_area(render_area) + .clear_values(&vk_clear_values) + .framebuffer(raw_framebuffer); + if caps.imageless_framebuffers { + vk_info = vk_info.push_next(&mut vk_attachment_info); + } + + self.device + .raw + .cmd_set_viewport(self.active, 0, &vk_viewports); + self.device + .raw + .cmd_set_scissor(self.active, 0, &vk_scissors); + self.device + .raw + .cmd_begin_render_pass(self.active, &vk_info, vk::SubpassContents::INLINE); + + self.bind_point = vk::PipelineBindPoint::GRAPHICS; + } + unsafe fn end_render_pass(&mut self) { + self.device.raw.cmd_end_render_pass(self.active); + } + + unsafe fn set_bind_group( + &mut self, + layout: &super::PipelineLayout, + index: u32, + group: &super::BindGroup, + dynamic_offsets: &[wgt::DynamicOffset], + ) { + let sets = [*group.set.raw()]; + self.device.raw.cmd_bind_descriptor_sets( + self.active, + self.bind_point, + layout.raw, + index, + &sets, + dynamic_offsets, + ); + } + unsafe fn set_push_constants( + &mut self, + layout: &super::PipelineLayout, + stages: wgt::ShaderStage, + offset: u32, + data: &[u32], + ) { + self.device.raw.cmd_push_constants( + self.active, + layout.raw, + conv::map_shader_stage(stages), + offset, + slice::from_raw_parts(data.as_ptr() as _, data.len() * 4), + ); + } + + unsafe fn insert_debug_marker(&mut self, label: &str) { + if let Some(ext) = self.device.debug_messenger() { + let cstr = self.temp.make_c_str(label); + let vk_label = vk::DebugUtilsLabelEXT::builder().label_name(&cstr).build(); + ext.cmd_insert_debug_utils_label(self.active, &vk_label); + } + } + unsafe fn begin_debug_marker(&mut self, group_label: &str) { + if let Some(ext) = self.device.debug_messenger() { + let cstr = self.temp.make_c_str(group_label); + let vk_label = vk::DebugUtilsLabelEXT::builder().label_name(cstr).build(); + ext.cmd_begin_debug_utils_label(self.active, &vk_label); + } + } + unsafe fn end_debug_marker(&mut self) { + if let Some(ext) = self.device.debug_messenger() { + ext.cmd_end_debug_utils_label(self.active); + } + } + + unsafe fn set_render_pipeline(&mut self, pipeline: &super::RenderPipeline) { + self.device.raw.cmd_bind_pipeline( + self.active, + vk::PipelineBindPoint::GRAPHICS, + pipeline.raw, + ); + } + + unsafe fn set_index_buffer<'a>( + &mut self, + binding: crate::BufferBinding<'a, super::Api>, + format: wgt::IndexFormat, + ) { + self.device.raw.cmd_bind_index_buffer( + self.active, + binding.buffer.raw, + binding.offset, + conv::map_index_format(format), + ); + } + unsafe fn set_vertex_buffer<'a>( + &mut self, + index: u32, + binding: crate::BufferBinding<'a, super::Api>, + ) { + let vk_buffers = [binding.buffer.raw]; + let vk_offsets = [binding.offset]; + self.device + .raw + .cmd_bind_vertex_buffers(self.active, index, &vk_buffers, &vk_offsets); + } + unsafe fn set_viewport(&mut self, rect: &crate::Rect, depth_range: Range) { + let vk_viewports = [vk::Viewport { + x: rect.x, + y: if self.device.private_caps.flip_y_requires_shift { + rect.y + rect.h + } else { + rect.y + }, + width: rect.w, + height: -rect.h, // flip Y + min_depth: depth_range.start, + max_depth: depth_range.end, + }]; + self.device + .raw + .cmd_set_viewport(self.active, 0, &vk_viewports); + } + unsafe fn set_scissor_rect(&mut self, rect: &crate::Rect) { + let vk_scissors = [vk::Rect2D { + offset: vk::Offset2D { + x: rect.x as i32, + y: rect.y as i32, + }, + extent: vk::Extent2D { + width: rect.w, + height: rect.h, + }, + }]; + self.device + .raw + .cmd_set_scissor(self.active, 0, &vk_scissors); + } + unsafe fn set_stencil_reference(&mut self, value: u32) { + self.device + .raw + .cmd_set_stencil_reference(self.active, vk::StencilFaceFlags::all(), value); + } + unsafe fn set_blend_constants(&mut self, color: &wgt::Color) { + let vk_constants = [ + color.r as f32, + color.g as f32, + color.b as f32, + color.a as f32, + ]; + self.device + .raw + .cmd_set_blend_constants(self.active, &vk_constants); + } + + unsafe fn draw( + &mut self, + start_vertex: u32, + vertex_count: u32, + start_instance: u32, + instance_count: u32, + ) { + self.device.raw.cmd_draw( + self.active, + vertex_count, + instance_count, + start_vertex, + start_instance, + ); + } + unsafe fn draw_indexed( + &mut self, + start_index: u32, + index_count: u32, + base_vertex: i32, + start_instance: u32, + instance_count: u32, + ) { + self.device.raw.cmd_draw_indexed( + self.active, + index_count, + instance_count, + start_index, + base_vertex, + start_instance, + ); + } + unsafe fn draw_indirect( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + draw_count: u32, + ) { + self.device.raw.cmd_draw_indirect( + self.active, + buffer.raw, + offset, + draw_count, + mem::size_of::() as u32, + ); + } + unsafe fn draw_indexed_indirect( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + draw_count: u32, + ) { + self.device.raw.cmd_draw_indexed_indirect( + self.active, + buffer.raw, + offset, + draw_count, + mem::size_of::() as u32, + ); + } + unsafe fn draw_indirect_count( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + count_buffer: &super::Buffer, + count_offset: wgt::BufferAddress, + max_count: u32, + ) { + let stride = mem::size_of::() as u32; + match self.device.extension_fns.draw_indirect_count { + Some(super::ExtensionFn::Extension(ref t)) => { + t.cmd_draw_indirect_count( + self.active, + buffer.raw, + offset, + count_buffer.raw, + count_offset, + max_count, + stride, + ); + } + Some(super::ExtensionFn::Promoted) => { + self.device.raw.cmd_draw_indirect_count( + self.active, + buffer.raw, + offset, + count_buffer.raw, + count_offset, + max_count, + stride, + ); + } + None => panic!("Feature `DRAW_INDIRECT_COUNT` not enabled"), + } + } + unsafe fn draw_indexed_indirect_count( + &mut self, + buffer: &super::Buffer, + offset: wgt::BufferAddress, + count_buffer: &super::Buffer, + count_offset: wgt::BufferAddress, + max_count: u32, + ) { + let stride = mem::size_of::() as u32; + match self.device.extension_fns.draw_indirect_count { + Some(super::ExtensionFn::Extension(ref t)) => { + t.cmd_draw_indexed_indirect_count( + self.active, + buffer.raw, + offset, + count_buffer.raw, + count_offset, + max_count, + stride, + ); + } + Some(super::ExtensionFn::Promoted) => { + self.device.raw.cmd_draw_indexed_indirect_count( + self.active, + buffer.raw, + offset, + count_buffer.raw, + count_offset, + max_count, + stride, + ); + } + None => panic!("Feature `DRAW_INDIRECT_COUNT` not enabled"), + } + } + + // compute + + unsafe fn begin_compute_pass(&mut self, desc: &crate::ComputePassDescriptor) { + self.bind_point = vk::PipelineBindPoint::COMPUTE; + self.begin_debug_marker(desc.label.unwrap_or_default()); + } + unsafe fn end_compute_pass(&mut self) { + self.end_debug_marker(); + } + + unsafe fn set_compute_pipeline(&mut self, pipeline: &super::ComputePipeline) { + self.device.raw.cmd_bind_pipeline( + self.active, + vk::PipelineBindPoint::COMPUTE, + pipeline.raw, + ); + } + + unsafe fn dispatch(&mut self, count: [u32; 3]) { + self.device + .raw + .cmd_dispatch(self.active, count[0], count[1], count[2]); + } + unsafe fn dispatch_indirect(&mut self, buffer: &super::Buffer, offset: wgt::BufferAddress) { + self.device + .raw + .cmd_dispatch_indirect(self.active, buffer.raw, offset) + } +} + +#[test] +fn check_dst_image_layout() { + assert_eq!( + conv::derive_image_layout(crate::TextureUse::COPY_DST, crate::FormatAspect::empty()), + DST_IMAGE_LAYOUT + ); +} diff --git a/wgpu-hal/src/vulkan/conv.rs b/wgpu-hal/src/vulkan/conv.rs new file mode 100644 index 0000000000..3ce8f67750 --- /dev/null +++ b/wgpu-hal/src/vulkan/conv.rs @@ -0,0 +1,776 @@ +use ash::vk; +use std::num::NonZeroU32; + +impl super::PrivateCapabilities { + pub fn map_texture_format(&self, format: wgt::TextureFormat) -> vk::Format { + use ash::vk::Format as F; + use wgt::TextureFormat as Tf; + match format { + Tf::R8Unorm => F::R8_UNORM, + Tf::R8Snorm => F::R8_SNORM, + Tf::R8Uint => F::R8_UINT, + Tf::R8Sint => F::R8_SINT, + Tf::R16Uint => F::R16_UINT, + Tf::R16Sint => F::R16_SINT, + Tf::R16Float => F::R16_SFLOAT, + Tf::Rg8Unorm => F::R8G8_UNORM, + Tf::Rg8Snorm => F::R8G8_SNORM, + Tf::Rg8Uint => F::R8G8_UINT, + Tf::Rg8Sint => F::R8G8_SINT, + Tf::R32Uint => F::R32_UINT, + Tf::R32Sint => F::R32_SINT, + Tf::R32Float => F::R32_SFLOAT, + Tf::Rg16Uint => F::R16G16_UINT, + Tf::Rg16Sint => F::R16G16_SINT, + Tf::Rg16Float => F::R16G16_SFLOAT, + Tf::Rgba8Unorm => F::R8G8B8A8_UNORM, + Tf::Rgba8UnormSrgb => F::R8G8B8A8_SRGB, + Tf::Bgra8UnormSrgb => F::B8G8R8A8_UNORM, + Tf::Rgba8Snorm => F::R8G8B8A8_SNORM, + Tf::Bgra8Unorm => F::B8G8R8A8_SNORM, + Tf::Rgba8Uint => F::R8G8B8A8_UINT, + Tf::Rgba8Sint => F::R8G8B8A8_SINT, + Tf::Rgb10a2Unorm => F::A2B10G10R10_UNORM_PACK32, + Tf::Rg11b10Float => F::B10G11R11_UFLOAT_PACK32, + Tf::Rg32Uint => F::R32G32_UINT, + Tf::Rg32Sint => F::R32G32_SINT, + Tf::Rg32Float => F::R32G32_SFLOAT, + Tf::Rgba16Uint => F::R16G16B16A16_UINT, + Tf::Rgba16Sint => F::R16G16B16A16_SINT, + Tf::Rgba16Float => F::R16G16B16A16_SFLOAT, + Tf::Rgba32Uint => F::R32G32B32A32_UINT, + Tf::Rgba32Sint => F::R32G32B32A32_SINT, + Tf::Rgba32Float => F::R32G32B32A32_SFLOAT, + Tf::Depth32Float => F::D32_SFLOAT, + Tf::Depth24Plus => { + if self.texture_d24 { + F::X8_D24_UNORM_PACK32 + } else { + F::D32_SFLOAT + } + } + Tf::Depth24PlusStencil8 => { + if self.texture_d24_s8 { + F::D24_UNORM_S8_UINT + } else { + F::D32_SFLOAT_S8_UINT + } + } + Tf::Bc1RgbaUnorm => F::BC1_RGBA_UNORM_BLOCK, + Tf::Bc1RgbaUnormSrgb => F::BC1_RGBA_SRGB_BLOCK, + Tf::Bc2RgbaUnorm => F::BC2_UNORM_BLOCK, + Tf::Bc2RgbaUnormSrgb => F::BC2_SRGB_BLOCK, + Tf::Bc3RgbaUnorm => F::BC3_UNORM_BLOCK, + Tf::Bc3RgbaUnormSrgb => F::BC3_SRGB_BLOCK, + Tf::Bc4RUnorm => F::BC4_UNORM_BLOCK, + Tf::Bc4RSnorm => F::BC4_SNORM_BLOCK, + Tf::Bc5RgUnorm => F::BC5_UNORM_BLOCK, + Tf::Bc5RgSnorm => F::BC5_SNORM_BLOCK, + Tf::Bc6hRgbSfloat => F::BC6H_SFLOAT_BLOCK, + Tf::Bc6hRgbUfloat => F::BC6H_UFLOAT_BLOCK, + Tf::Bc7RgbaUnorm => F::BC7_UNORM_BLOCK, + Tf::Bc7RgbaUnormSrgb => F::BC7_SRGB_BLOCK, + Tf::Etc2RgbUnorm => F::ETC2_R8G8B8_UNORM_BLOCK, + Tf::Etc2RgbUnormSrgb => F::ETC2_R8G8B8_SRGB_BLOCK, + Tf::Etc2RgbA1Unorm => F::ETC2_R8G8B8A1_UNORM_BLOCK, + Tf::Etc2RgbA1UnormSrgb => F::ETC2_R8G8B8A1_SRGB_BLOCK, + Tf::EacRUnorm => F::EAC_R11_UNORM_BLOCK, + Tf::EacRSnorm => F::EAC_R11_SNORM_BLOCK, + Tf::EtcRgUnorm => F::EAC_R11G11_UNORM_BLOCK, + Tf::EtcRgSnorm => F::EAC_R11G11_SNORM_BLOCK, + Tf::Astc4x4RgbaUnorm => F::ASTC_4X4_UNORM_BLOCK, + Tf::Astc4x4RgbaUnormSrgb => F::ASTC_4X4_SRGB_BLOCK, + Tf::Astc5x4RgbaUnorm => F::ASTC_5X4_UNORM_BLOCK, + Tf::Astc5x4RgbaUnormSrgb => F::ASTC_5X4_SRGB_BLOCK, + Tf::Astc5x5RgbaUnorm => F::ASTC_5X5_UNORM_BLOCK, + Tf::Astc5x5RgbaUnormSrgb => F::ASTC_5X5_SRGB_BLOCK, + Tf::Astc6x5RgbaUnorm => F::ASTC_6X5_UNORM_BLOCK, + Tf::Astc6x5RgbaUnormSrgb => F::ASTC_6X5_SRGB_BLOCK, + Tf::Astc6x6RgbaUnorm => F::ASTC_6X6_UNORM_BLOCK, + Tf::Astc6x6RgbaUnormSrgb => F::ASTC_6X6_SRGB_BLOCK, + Tf::Astc8x5RgbaUnorm => F::ASTC_8X5_UNORM_BLOCK, + Tf::Astc8x5RgbaUnormSrgb => F::ASTC_8X5_SRGB_BLOCK, + Tf::Astc8x6RgbaUnorm => F::ASTC_8X6_UNORM_BLOCK, + Tf::Astc8x6RgbaUnormSrgb => F::ASTC_8X6_SRGB_BLOCK, + Tf::Astc10x5RgbaUnorm => F::ASTC_8X8_UNORM_BLOCK, + Tf::Astc10x5RgbaUnormSrgb => F::ASTC_8X8_SRGB_BLOCK, + Tf::Astc10x6RgbaUnorm => F::ASTC_10X5_UNORM_BLOCK, + Tf::Astc10x6RgbaUnormSrgb => F::ASTC_10X5_SRGB_BLOCK, + Tf::Astc8x8RgbaUnorm => F::ASTC_10X6_UNORM_BLOCK, + Tf::Astc8x8RgbaUnormSrgb => F::ASTC_10X6_SRGB_BLOCK, + Tf::Astc10x8RgbaUnorm => F::ASTC_10X8_UNORM_BLOCK, + Tf::Astc10x8RgbaUnormSrgb => F::ASTC_10X8_SRGB_BLOCK, + Tf::Astc10x10RgbaUnorm => F::ASTC_10X10_UNORM_BLOCK, + Tf::Astc10x10RgbaUnormSrgb => F::ASTC_10X10_SRGB_BLOCK, + Tf::Astc12x10RgbaUnorm => F::ASTC_12X10_UNORM_BLOCK, + Tf::Astc12x10RgbaUnormSrgb => F::ASTC_12X10_SRGB_BLOCK, + Tf::Astc12x12RgbaUnorm => F::ASTC_12X12_UNORM_BLOCK, + Tf::Astc12x12RgbaUnormSrgb => F::ASTC_12X12_SRGB_BLOCK, + } + } +} + +impl crate::Attachment<'_, super::Api> { + pub(super) fn make_attachment_key( + &self, + ops: crate::AttachmentOp, + caps: &super::PrivateCapabilities, + ) -> super::AttachmentKey { + let aspects = self.view.aspects(); + super::AttachmentKey { + format: caps.map_texture_format(self.view.attachment.view_format), + layout_pre: derive_image_layout(self.boundary_usage.start, aspects), + layout_in: derive_image_layout(self.usage, aspects), + layout_post: derive_image_layout(self.boundary_usage.end, aspects), + ops, + } + } +} + +impl crate::ColorAttachment<'_, super::Api> { + pub(super) unsafe fn make_vk_clear_color(&self) -> vk::ClearColorValue { + let cv = &self.clear_value; + match self + .target + .view + .attachment + .view_format + .describe() + .sample_type + { + wgt::TextureSampleType::Float { .. } | wgt::TextureSampleType::Depth => { + vk::ClearColorValue { + float32: [cv.r as f32, cv.g as f32, cv.b as f32, cv.a as f32], + } + } + wgt::TextureSampleType::Sint => vk::ClearColorValue { + int32: [cv.r as i32, cv.g as i32, cv.b as i32, cv.a as i32], + }, + wgt::TextureSampleType::Uint => vk::ClearColorValue { + uint32: [cv.r as u32, cv.g as u32, cv.b as u32, cv.a as u32], + }, + } + } +} + +pub fn derive_image_layout( + usage: crate::TextureUse, + aspects: crate::FormatAspect, +) -> vk::ImageLayout { + //Note: depth textures are always sampled with RODS layout + let is_color = aspects.contains(crate::FormatAspect::COLOR); + match usage { + crate::TextureUse::UNINITIALIZED => vk::ImageLayout::UNDEFINED, + crate::TextureUse::COPY_SRC => vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + crate::TextureUse::COPY_DST => vk::ImageLayout::TRANSFER_DST_OPTIMAL, + crate::TextureUse::SAMPLED if is_color => vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL, + crate::TextureUse::COLOR_TARGET => vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + crate::TextureUse::DEPTH_STENCIL_WRITE => vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL, + _ => { + if usage.is_empty() { + vk::ImageLayout::PRESENT_SRC_KHR + } else if is_color { + vk::ImageLayout::GENERAL + } else { + vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL + } + } + } +} + +pub fn map_texture_usage(usage: crate::TextureUse) -> vk::ImageUsageFlags { + let mut flags = vk::ImageUsageFlags::empty(); + if usage.contains(crate::TextureUse::COPY_SRC) { + flags |= vk::ImageUsageFlags::TRANSFER_SRC; + } + if usage.contains(crate::TextureUse::COPY_DST) { + flags |= vk::ImageUsageFlags::TRANSFER_DST; + } + if usage.contains(crate::TextureUse::SAMPLED) { + flags |= vk::ImageUsageFlags::SAMPLED; + } + if usage.contains(crate::TextureUse::COLOR_TARGET) { + flags |= vk::ImageUsageFlags::COLOR_ATTACHMENT; + } + if usage + .intersects(crate::TextureUse::DEPTH_STENCIL_READ | crate::TextureUse::DEPTH_STENCIL_WRITE) + { + flags |= vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT; + } + if usage.intersects(crate::TextureUse::STORAGE_LOAD | crate::TextureUse::STORAGE_STORE) { + flags |= vk::ImageUsageFlags::STORAGE; + } + flags +} + +pub fn map_texture_usage_to_barrier( + usage: crate::TextureUse, +) -> (vk::PipelineStageFlags, vk::AccessFlags) { + let mut stages = vk::PipelineStageFlags::empty(); + let mut access = vk::AccessFlags::empty(); + let shader_stages = vk::PipelineStageFlags::VERTEX_SHADER + | vk::PipelineStageFlags::FRAGMENT_SHADER + | vk::PipelineStageFlags::COMPUTE_SHADER; + + if usage.contains(crate::TextureUse::COPY_SRC) { + stages |= vk::PipelineStageFlags::TRANSFER; + access |= vk::AccessFlags::TRANSFER_READ; + } + if usage.contains(crate::TextureUse::COPY_DST) { + stages |= vk::PipelineStageFlags::TRANSFER; + access |= vk::AccessFlags::TRANSFER_WRITE; + } + if usage.contains(crate::TextureUse::SAMPLED) { + stages |= shader_stages; + access |= vk::AccessFlags::SHADER_READ; + } + if usage.contains(crate::TextureUse::COLOR_TARGET) { + stages |= vk::PipelineStageFlags::COLOR_ATTACHMENT_OUTPUT; + access |= vk::AccessFlags::COLOR_ATTACHMENT_READ | vk::AccessFlags::COLOR_ATTACHMENT_WRITE; + } + if usage.intersects(crate::TextureUse::DEPTH_STENCIL_READ) { + stages |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS + | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS; + access |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ; + } + if usage.intersects(crate::TextureUse::DEPTH_STENCIL_WRITE) { + stages |= vk::PipelineStageFlags::EARLY_FRAGMENT_TESTS + | vk::PipelineStageFlags::LATE_FRAGMENT_TESTS; + access |= vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_READ + | vk::AccessFlags::DEPTH_STENCIL_ATTACHMENT_WRITE; + } + if usage.contains(crate::TextureUse::STORAGE_LOAD) { + stages |= shader_stages; + access |= vk::AccessFlags::SHADER_READ; + } + if usage.contains(crate::TextureUse::STORAGE_STORE) { + stages |= shader_stages; + access |= vk::AccessFlags::SHADER_WRITE; + } + + if usage == crate::TextureUse::UNINITIALIZED { + ( + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::AccessFlags::empty(), + ) + } else { + (stages, access) + } +} + +pub fn map_vk_image_usage(usage: vk::ImageUsageFlags) -> crate::TextureUse { + let mut bits = crate::TextureUse::empty(); + if usage.contains(vk::ImageUsageFlags::TRANSFER_SRC) { + bits |= crate::TextureUse::COPY_SRC; + } + if usage.contains(vk::ImageUsageFlags::TRANSFER_DST) { + bits |= crate::TextureUse::COPY_DST; + } + if usage.contains(vk::ImageUsageFlags::SAMPLED) { + bits |= crate::TextureUse::SAMPLED; + } + if usage.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) { + bits |= crate::TextureUse::COLOR_TARGET; + } + if usage.contains(vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT) { + bits |= crate::TextureUse::DEPTH_STENCIL_READ | crate::TextureUse::DEPTH_STENCIL_WRITE; + } + if usage.contains(vk::ImageUsageFlags::STORAGE) { + bits |= crate::TextureUse::STORAGE_LOAD | crate::TextureUse::STORAGE_STORE; + } + bits +} + +pub fn map_texture_dimension(dim: wgt::TextureDimension) -> vk::ImageType { + match dim { + wgt::TextureDimension::D1 => vk::ImageType::TYPE_1D, + wgt::TextureDimension::D2 => vk::ImageType::TYPE_2D, + wgt::TextureDimension::D3 => vk::ImageType::TYPE_3D, + } +} + +pub fn map_index_format(index_format: wgt::IndexFormat) -> vk::IndexType { + match index_format { + wgt::IndexFormat::Uint16 => vk::IndexType::UINT16, + wgt::IndexFormat::Uint32 => vk::IndexType::UINT32, + } +} + +pub fn map_vertex_format(vertex_format: wgt::VertexFormat) -> vk::Format { + use wgt::VertexFormat as Vf; + match vertex_format { + Vf::Uint8x2 => vk::Format::R8G8_UINT, + Vf::Uint8x4 => vk::Format::R8G8B8A8_UINT, + Vf::Sint8x2 => vk::Format::R8G8_SINT, + Vf::Sint8x4 => vk::Format::R8G8B8A8_SINT, + Vf::Unorm8x2 => vk::Format::R8G8_UNORM, + Vf::Unorm8x4 => vk::Format::R8G8B8A8_UNORM, + Vf::Snorm8x2 => vk::Format::R8G8_SNORM, + Vf::Snorm8x4 => vk::Format::R8G8B8A8_SNORM, + Vf::Uint16x2 => vk::Format::R16G16_UINT, + Vf::Uint16x4 => vk::Format::R16G16B16A16_UINT, + Vf::Sint16x2 => vk::Format::R16G16_SINT, + Vf::Sint16x4 => vk::Format::R16G16B16A16_SINT, + Vf::Unorm16x2 => vk::Format::R16G16_UNORM, + Vf::Unorm16x4 => vk::Format::R16G16B16A16_UNORM, + Vf::Snorm16x2 => vk::Format::R16G16_SNORM, + Vf::Snorm16x4 => vk::Format::R16G16B16A16_SNORM, + Vf::Float16x2 => vk::Format::R16G16_SFLOAT, + Vf::Float16x4 => vk::Format::R16G16B16A16_SFLOAT, + Vf::Float32 => vk::Format::R32_SFLOAT, + Vf::Float32x2 => vk::Format::R32G32_SFLOAT, + Vf::Float32x3 => vk::Format::R32G32B32_SFLOAT, + Vf::Float32x4 => vk::Format::R32G32B32A32_SFLOAT, + Vf::Uint32 => vk::Format::R32_UINT, + Vf::Uint32x2 => vk::Format::R32G32_UINT, + Vf::Uint32x3 => vk::Format::R32G32B32_UINT, + Vf::Uint32x4 => vk::Format::R32G32B32A32_UINT, + Vf::Sint32 => vk::Format::R32_SINT, + Vf::Sint32x2 => vk::Format::R32G32_SINT, + Vf::Sint32x3 => vk::Format::R32G32B32_SINT, + Vf::Sint32x4 => vk::Format::R32G32B32A32_SINT, + Vf::Float64 => vk::Format::R64_SFLOAT, + Vf::Float64x2 => vk::Format::R64G64_SFLOAT, + Vf::Float64x3 => vk::Format::R64G64B64_SFLOAT, + Vf::Float64x4 => vk::Format::R64G64B64A64_SFLOAT, + } +} + +pub fn map_aspects(aspects: crate::FormatAspect) -> vk::ImageAspectFlags { + let mut flags = vk::ImageAspectFlags::empty(); + if aspects.contains(crate::FormatAspect::COLOR) { + flags |= vk::ImageAspectFlags::COLOR; + } + if aspects.contains(crate::FormatAspect::DEPTH) { + flags |= vk::ImageAspectFlags::DEPTH; + } + if aspects.contains(crate::FormatAspect::STENCIL) { + flags |= vk::ImageAspectFlags::STENCIL; + } + flags +} + +pub fn map_origin( + origin: wgt::Origin3d, + texture_dim: wgt::TextureDimension, +) -> (u32, vk::Offset3D) { + let (z, array_layer) = match texture_dim { + wgt::TextureDimension::D3 => (origin.z as i32, 0), + _ => (0, origin.z), + }; + ( + array_layer, + vk::Offset3D { + x: origin.x as i32, + y: origin.y as i32, + z, + }, + ) +} + +pub fn map_extent( + extent: wgt::Extent3d, + texture_dim: wgt::TextureDimension, +) -> (u32, vk::Extent3D) { + let (depth, array_layers) = match texture_dim { + wgt::TextureDimension::D3 => (extent.depth_or_array_layers, 1), + _ => (1, extent.depth_or_array_layers), + }; + ( + array_layers, + vk::Extent3D { + width: extent.width, + height: extent.height, + depth, + }, + ) +} + +pub fn map_attachment_ops( + op: crate::AttachmentOp, +) -> (vk::AttachmentLoadOp, vk::AttachmentStoreOp) { + let load_op = if op.contains(crate::AttachmentOp::LOAD) { + vk::AttachmentLoadOp::LOAD + } else { + vk::AttachmentLoadOp::CLEAR + }; + let store_op = if op.contains(crate::AttachmentOp::STORE) { + vk::AttachmentStoreOp::STORE + } else { + vk::AttachmentStoreOp::DONT_CARE + }; + (load_op, store_op) +} + +pub fn map_present_mode(mode: wgt::PresentMode) -> vk::PresentModeKHR { + match mode { + wgt::PresentMode::Immediate => vk::PresentModeKHR::IMMEDIATE, + wgt::PresentMode::Mailbox => vk::PresentModeKHR::MAILBOX, + wgt::PresentMode::Fifo => vk::PresentModeKHR::FIFO, + //wgt::PresentMode::Relaxed => vk::PresentModeKHR::FIFO_RELAXED, + } +} + +pub fn map_vk_present_mode(mode: vk::PresentModeKHR) -> Option { + if mode == vk::PresentModeKHR::IMMEDIATE { + Some(wgt::PresentMode::Immediate) + } else if mode == vk::PresentModeKHR::MAILBOX { + Some(wgt::PresentMode::Mailbox) + } else if mode == vk::PresentModeKHR::FIFO { + Some(wgt::PresentMode::Fifo) + } else if mode == vk::PresentModeKHR::FIFO_RELAXED { + //Some(wgt::PresentMode::Relaxed) + None + } else { + log::warn!("Unrecognized present mode {:?}", mode); + None + } +} + +pub fn map_composite_alpha_mode(mode: crate::CompositeAlphaMode) -> vk::CompositeAlphaFlagsKHR { + match mode { + crate::CompositeAlphaMode::Opaque => vk::CompositeAlphaFlagsKHR::OPAQUE, + crate::CompositeAlphaMode::PostMultiplied => vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED, + crate::CompositeAlphaMode::PreMultiplied => vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED, + } +} + +pub fn map_vk_composite_alpha(flags: vk::CompositeAlphaFlagsKHR) -> Vec { + let mut modes = Vec::new(); + if flags.contains(vk::CompositeAlphaFlagsKHR::OPAQUE) { + modes.push(crate::CompositeAlphaMode::Opaque); + } + if flags.contains(vk::CompositeAlphaFlagsKHR::POST_MULTIPLIED) { + modes.push(crate::CompositeAlphaMode::PostMultiplied); + } + if flags.contains(vk::CompositeAlphaFlagsKHR::PRE_MULTIPLIED) { + modes.push(crate::CompositeAlphaMode::PreMultiplied); + } + modes +} + +pub fn map_buffer_usage(usage: crate::BufferUse) -> vk::BufferUsageFlags { + let mut flags = vk::BufferUsageFlags::empty(); + if usage.contains(crate::BufferUse::COPY_SRC) { + flags |= vk::BufferUsageFlags::TRANSFER_SRC; + } + if usage.contains(crate::BufferUse::COPY_DST) { + flags |= vk::BufferUsageFlags::TRANSFER_DST; + } + if usage.contains(crate::BufferUse::UNIFORM) { + flags |= vk::BufferUsageFlags::UNIFORM_BUFFER; + } + if usage.intersects(crate::BufferUse::STORAGE_LOAD | crate::BufferUse::STORAGE_STORE) { + flags |= vk::BufferUsageFlags::STORAGE_BUFFER; + } + if usage.contains(crate::BufferUse::INDEX) { + flags |= vk::BufferUsageFlags::INDEX_BUFFER; + } + if usage.contains(crate::BufferUse::VERTEX) { + flags |= vk::BufferUsageFlags::VERTEX_BUFFER; + } + if usage.contains(crate::BufferUse::INDIRECT) { + flags |= vk::BufferUsageFlags::INDIRECT_BUFFER; + } + flags +} + +pub fn map_buffer_usage_to_barrier( + usage: crate::BufferUse, +) -> (vk::PipelineStageFlags, vk::AccessFlags) { + let mut stages = vk::PipelineStageFlags::empty(); + let mut access = vk::AccessFlags::empty(); + let shader_stages = vk::PipelineStageFlags::VERTEX_SHADER + | vk::PipelineStageFlags::FRAGMENT_SHADER + | vk::PipelineStageFlags::COMPUTE_SHADER; + + if usage.contains(crate::BufferUse::MAP_READ) { + stages |= vk::PipelineStageFlags::HOST; + access |= vk::AccessFlags::HOST_READ; + } + if usage.contains(crate::BufferUse::MAP_WRITE) { + stages |= vk::PipelineStageFlags::HOST; + access |= vk::AccessFlags::HOST_WRITE; + } + if usage.contains(crate::BufferUse::COPY_SRC) { + stages |= vk::PipelineStageFlags::TRANSFER; + access |= vk::AccessFlags::TRANSFER_READ; + } + if usage.contains(crate::BufferUse::COPY_DST) { + stages |= vk::PipelineStageFlags::TRANSFER; + access |= vk::AccessFlags::TRANSFER_WRITE; + } + if usage.contains(crate::BufferUse::UNIFORM) { + stages |= shader_stages; + access |= vk::AccessFlags::UNIFORM_READ; + } + if usage.intersects(crate::BufferUse::STORAGE_LOAD) { + stages |= shader_stages; + access |= vk::AccessFlags::SHADER_READ; + } + if usage.intersects(crate::BufferUse::STORAGE_STORE) { + stages |= shader_stages; + access |= vk::AccessFlags::SHADER_WRITE; + } + if usage.contains(crate::BufferUse::INDEX) { + stages |= vk::PipelineStageFlags::VERTEX_INPUT; + access |= vk::AccessFlags::INDEX_READ; + } + if usage.contains(crate::BufferUse::VERTEX) { + stages |= vk::PipelineStageFlags::VERTEX_INPUT; + access |= vk::AccessFlags::VERTEX_ATTRIBUTE_READ; + } + if usage.contains(crate::BufferUse::INDIRECT) { + stages |= vk::PipelineStageFlags::VERTEX_INPUT; + access |= vk::AccessFlags::INDIRECT_COMMAND_READ; + } + + (stages, access) +} + +pub fn map_view_dimension(dim: wgt::TextureViewDimension) -> vk::ImageViewType { + match dim { + wgt::TextureViewDimension::D1 => vk::ImageViewType::TYPE_1D, + wgt::TextureViewDimension::D2 => vk::ImageViewType::TYPE_2D, + wgt::TextureViewDimension::D2Array => vk::ImageViewType::TYPE_2D_ARRAY, + wgt::TextureViewDimension::Cube => vk::ImageViewType::CUBE, + wgt::TextureViewDimension::CubeArray => vk::ImageViewType::CUBE_ARRAY, + wgt::TextureViewDimension::D3 => vk::ImageViewType::TYPE_3D, + } +} + +pub fn map_subresource_range( + range: &wgt::ImageSubresourceRange, + texture_aspect: crate::FormatAspect, +) -> vk::ImageSubresourceRange { + vk::ImageSubresourceRange { + aspect_mask: map_aspects(crate::FormatAspect::from(range.aspect) & texture_aspect), + base_mip_level: range.base_mip_level, + level_count: range + .mip_level_count + .map_or(vk::REMAINING_MIP_LEVELS, NonZeroU32::get), + base_array_layer: range.base_array_layer, + layer_count: range + .array_layer_count + .map_or(vk::REMAINING_ARRAY_LAYERS, NonZeroU32::get), + } +} + +pub fn map_subresource_layers( + base: &crate::TextureCopyBase, + texture_dim: wgt::TextureDimension, + texture_aspect: crate::FormatAspect, + layer_count: u32, +) -> (vk::ImageSubresourceLayers, vk::Offset3D) { + let (base_array_layer, offset) = map_origin(base.origin, texture_dim); + let subresource = vk::ImageSubresourceLayers { + aspect_mask: map_aspects(base.aspect & texture_aspect), + mip_level: base.mip_level, + base_array_layer, + layer_count, + }; + (subresource, offset) +} + +pub fn map_filter_mode(mode: wgt::FilterMode) -> vk::Filter { + match mode { + wgt::FilterMode::Nearest => vk::Filter::NEAREST, + wgt::FilterMode::Linear => vk::Filter::LINEAR, + } +} + +pub fn map_mip_filter_mode(mode: wgt::FilterMode) -> vk::SamplerMipmapMode { + match mode { + wgt::FilterMode::Nearest => vk::SamplerMipmapMode::NEAREST, + wgt::FilterMode::Linear => vk::SamplerMipmapMode::LINEAR, + } +} + +pub fn map_address_mode(mode: wgt::AddressMode) -> vk::SamplerAddressMode { + match mode { + wgt::AddressMode::ClampToEdge => vk::SamplerAddressMode::CLAMP_TO_EDGE, + wgt::AddressMode::Repeat => vk::SamplerAddressMode::REPEAT, + wgt::AddressMode::MirrorRepeat => vk::SamplerAddressMode::MIRRORED_REPEAT, + wgt::AddressMode::ClampToBorder => vk::SamplerAddressMode::CLAMP_TO_BORDER, + //wgt::AddressMode::MirrorClamp => vk::SamplerAddressMode::MIRROR_CLAMP_TO_EDGE, + } +} + +pub fn map_border_color(border_color: wgt::SamplerBorderColor) -> vk::BorderColor { + match border_color { + wgt::SamplerBorderColor::TransparentBlack => vk::BorderColor::FLOAT_TRANSPARENT_BLACK, + wgt::SamplerBorderColor::OpaqueBlack => vk::BorderColor::FLOAT_OPAQUE_BLACK, + wgt::SamplerBorderColor::OpaqueWhite => vk::BorderColor::FLOAT_OPAQUE_WHITE, + } +} + +pub fn map_comparison(fun: wgt::CompareFunction) -> vk::CompareOp { + use wgt::CompareFunction as Cf; + match fun { + Cf::Never => vk::CompareOp::NEVER, + Cf::Less => vk::CompareOp::LESS, + Cf::LessEqual => vk::CompareOp::LESS_OR_EQUAL, + Cf::Equal => vk::CompareOp::EQUAL, + Cf::GreaterEqual => vk::CompareOp::GREATER_OR_EQUAL, + Cf::Greater => vk::CompareOp::GREATER, + Cf::NotEqual => vk::CompareOp::NOT_EQUAL, + Cf::Always => vk::CompareOp::ALWAYS, + } +} + +pub fn map_shader_stage(stage: wgt::ShaderStage) -> vk::ShaderStageFlags { + let mut flags = vk::ShaderStageFlags::empty(); + if stage.contains(wgt::ShaderStage::VERTEX) { + flags |= vk::ShaderStageFlags::VERTEX; + } + if stage.contains(wgt::ShaderStage::FRAGMENT) { + flags |= vk::ShaderStageFlags::FRAGMENT; + } + if stage.contains(wgt::ShaderStage::COMPUTE) { + flags |= vk::ShaderStageFlags::COMPUTE; + } + flags +} + +pub fn map_binding_type(ty: wgt::BindingType) -> vk::DescriptorType { + match ty { + wgt::BindingType::Buffer { + ty, + has_dynamic_offset, + .. + } => match ty { + wgt::BufferBindingType::Storage { .. } => match has_dynamic_offset { + true => vk::DescriptorType::STORAGE_BUFFER_DYNAMIC, + false => vk::DescriptorType::STORAGE_BUFFER, + }, + wgt::BufferBindingType::Uniform => match has_dynamic_offset { + true => vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC, + false => vk::DescriptorType::UNIFORM_BUFFER, + }, + }, + wgt::BindingType::Sampler { .. } => vk::DescriptorType::SAMPLER, + wgt::BindingType::Texture { .. } => vk::DescriptorType::SAMPLED_IMAGE, + wgt::BindingType::StorageTexture { .. } => vk::DescriptorType::STORAGE_IMAGE, + } +} + +pub fn map_topology(topology: wgt::PrimitiveTopology) -> vk::PrimitiveTopology { + use wgt::PrimitiveTopology as Pt; + match topology { + Pt::PointList => vk::PrimitiveTopology::POINT_LIST, + Pt::LineList => vk::PrimitiveTopology::LINE_LIST, + Pt::LineStrip => vk::PrimitiveTopology::LINE_STRIP, + Pt::TriangleList => vk::PrimitiveTopology::TRIANGLE_LIST, + Pt::TriangleStrip => vk::PrimitiveTopology::TRIANGLE_STRIP, + } +} + +pub fn map_polygon_mode(mode: wgt::PolygonMode) -> vk::PolygonMode { + match mode { + wgt::PolygonMode::Fill => vk::PolygonMode::FILL, + wgt::PolygonMode::Line => vk::PolygonMode::LINE, + wgt::PolygonMode::Point => vk::PolygonMode::POINT, + } +} + +pub fn map_front_face(front_face: wgt::FrontFace) -> vk::FrontFace { + match front_face { + wgt::FrontFace::Cw => vk::FrontFace::CLOCKWISE, + wgt::FrontFace::Ccw => vk::FrontFace::COUNTER_CLOCKWISE, + } +} + +pub fn map_cull_face(face: wgt::Face) -> vk::CullModeFlags { + match face { + wgt::Face::Front => vk::CullModeFlags::FRONT, + wgt::Face::Back => vk::CullModeFlags::BACK, + } +} + +pub fn map_stencil_op(op: wgt::StencilOperation) -> vk::StencilOp { + use wgt::StencilOperation as So; + match op { + So::Keep => vk::StencilOp::KEEP, + So::Zero => vk::StencilOp::ZERO, + So::Replace => vk::StencilOp::REPLACE, + So::Invert => vk::StencilOp::INVERT, + So::IncrementClamp => vk::StencilOp::INCREMENT_AND_CLAMP, + So::IncrementWrap => vk::StencilOp::INCREMENT_AND_WRAP, + So::DecrementClamp => vk::StencilOp::DECREMENT_AND_CLAMP, + So::DecrementWrap => vk::StencilOp::DECREMENT_AND_WRAP, + } +} + +pub fn map_stencil_face(face: &wgt::StencilFaceState) -> vk::StencilOpState { + vk::StencilOpState { + fail_op: map_stencil_op(face.fail_op), + pass_op: map_stencil_op(face.pass_op), + depth_fail_op: map_stencil_op(face.depth_fail_op), + compare_op: map_comparison(face.compare), + compare_mask: !0, + write_mask: !0, + reference: 0, + } +} + +fn map_blend_factor(factor: wgt::BlendFactor) -> vk::BlendFactor { + use wgt::BlendFactor as Bf; + match factor { + Bf::Zero => vk::BlendFactor::ZERO, + Bf::One => vk::BlendFactor::ONE, + Bf::Src => vk::BlendFactor::SRC_COLOR, + Bf::OneMinusSrc => vk::BlendFactor::ONE_MINUS_SRC_COLOR, + Bf::SrcAlpha => vk::BlendFactor::SRC_ALPHA, + Bf::OneMinusSrcAlpha => vk::BlendFactor::ONE_MINUS_SRC_ALPHA, + Bf::Dst => vk::BlendFactor::DST_COLOR, + Bf::OneMinusDst => vk::BlendFactor::ONE_MINUS_DST_COLOR, + Bf::DstAlpha => vk::BlendFactor::DST_ALPHA, + Bf::OneMinusDstAlpha => vk::BlendFactor::ONE_MINUS_DST_ALPHA, + Bf::SrcAlphaSaturated => vk::BlendFactor::SRC_ALPHA_SATURATE, + Bf::Constant => vk::BlendFactor::CONSTANT_COLOR, + Bf::OneMinusConstant => vk::BlendFactor::ONE_MINUS_CONSTANT_COLOR, + } +} + +fn map_blend_op(operation: wgt::BlendOperation) -> vk::BlendOp { + use wgt::BlendOperation as Bo; + match operation { + Bo::Add => vk::BlendOp::ADD, + Bo::Subtract => vk::BlendOp::SUBTRACT, + Bo::ReverseSubtract => vk::BlendOp::REVERSE_SUBTRACT, + Bo::Min => vk::BlendOp::MIN, + Bo::Max => vk::BlendOp::MAX, + } +} + +pub fn map_blend_component( + component: &wgt::BlendComponent, +) -> (vk::BlendOp, vk::BlendFactor, vk::BlendFactor) { + let op = map_blend_op(component.operation); + let src = map_blend_factor(component.src_factor); + let dst = map_blend_factor(component.dst_factor); + (op, src, dst) +} + +pub fn map_pipeline_statistics( + types: wgt::PipelineStatisticsTypes, +) -> vk::QueryPipelineStatisticFlags { + use wgt::PipelineStatisticsTypes as Pst; + let mut flags = vk::QueryPipelineStatisticFlags::empty(); + if types.contains(Pst::VERTEX_SHADER_INVOCATIONS) { + flags |= vk::QueryPipelineStatisticFlags::VERTEX_SHADER_INVOCATIONS; + } + if types.contains(Pst::CLIPPER_INVOCATIONS) { + flags |= vk::QueryPipelineStatisticFlags::CLIPPING_INVOCATIONS; + } + if types.contains(Pst::CLIPPER_PRIMITIVES_OUT) { + flags |= vk::QueryPipelineStatisticFlags::CLIPPING_PRIMITIVES; + } + if types.contains(Pst::FRAGMENT_SHADER_INVOCATIONS) { + flags |= vk::QueryPipelineStatisticFlags::FRAGMENT_SHADER_INVOCATIONS; + } + if types.contains(Pst::COMPUTE_SHADER_INVOCATIONS) { + flags |= vk::QueryPipelineStatisticFlags::COMPUTE_SHADER_INVOCATIONS; + } + flags +} diff --git a/wgpu-hal/src/vulkan/device.rs b/wgpu-hal/src/vulkan/device.rs new file mode 100644 index 0000000000..8657d40cf9 --- /dev/null +++ b/wgpu-hal/src/vulkan/device.rs @@ -0,0 +1,1450 @@ +use super::conv; + +use arrayvec::ArrayVec; +use ash::{extensions::khr, version::DeviceV1_0, vk}; +use inplace_it::inplace_or_alloc_from_iter; +use parking_lot::Mutex; + +use std::{cmp, collections::hash_map::Entry, ffi::CString, ptr, sync::Arc}; + +impl super::DeviceShared { + pub(super) unsafe fn set_object_name( + &self, + object_type: vk::ObjectType, + object: impl vk::Handle, + name: &str, + ) { + use std::ffi::CStr; + + let extension = match self.instance.debug_utils { + Some(ref debug_utils) => &debug_utils.extension, + None => return, + }; + + // Keep variables outside the if-else block to ensure they do not + // go out of scope while we hold a pointer to them + let mut buffer: [u8; 64] = [0u8; 64]; + let buffer_vec: Vec; + + // Append a null terminator to the string + let name_bytes = if name.len() < buffer.len() { + // Common case, string is very small. Allocate a copy on the stack. + buffer[..name.len()].copy_from_slice(name.as_bytes()); + // Add null terminator + buffer[name.len()] = 0; + &buffer[..name.len() + 1] + } else { + // Less common case, the string is large. + // This requires a heap allocation. + buffer_vec = name + .as_bytes() + .iter() + .cloned() + .chain(std::iter::once(0)) + .collect(); + &buffer_vec + }; + + let _result = extension.debug_utils_set_object_name( + self.raw.handle(), + &vk::DebugUtilsObjectNameInfoEXT::builder() + .object_type(object_type) + .object_handle(object.as_raw()) + .object_name(CStr::from_bytes_with_nul_unchecked(name_bytes)), + ); + } + + pub fn make_render_pass( + &self, + key: super::RenderPassKey, + ) -> Result { + Ok(match self.render_passes.lock().entry(key) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let mut vk_attachments = Vec::new(); + let mut color_refs = Vec::with_capacity(e.key().colors.len()); + let mut resolve_refs = Vec::with_capacity(color_refs.capacity()); + let mut ds_ref = None; + let samples = vk::SampleCountFlags::from_raw(e.key().sample_count); + + for cat in e.key().colors.iter() { + color_refs.push(vk::AttachmentReference { + attachment: vk_attachments.len() as u32, + layout: cat.base.layout_in, + }); + vk_attachments.push({ + let (load_op, store_op) = conv::map_attachment_ops(cat.base.ops); + vk::AttachmentDescription::builder() + .format(cat.base.format) + .samples(samples) + .load_op(load_op) + .store_op(store_op) + .initial_layout(cat.base.layout_pre) + .final_layout(cat.base.layout_post) + .build() + }); + let at_ref = if let Some(ref rat) = cat.resolve { + let at_ref = vk::AttachmentReference { + attachment: vk_attachments.len() as u32, + layout: rat.layout_in, + }; + let (load_op, store_op) = conv::map_attachment_ops(rat.ops); + let vk_attachment = vk::AttachmentDescription::builder() + .format(rat.format) + .samples(vk::SampleCountFlags::TYPE_1) + .load_op(load_op) + .store_op(store_op) + .initial_layout(rat.layout_pre) + .final_layout(rat.layout_post) + .build(); + vk_attachments.push(vk_attachment); + at_ref + } else { + vk::AttachmentReference { + attachment: vk::ATTACHMENT_UNUSED, + layout: vk::ImageLayout::UNDEFINED, + } + }; + resolve_refs.push(at_ref); + } + + if let Some(ref ds) = e.key().depth_stencil { + ds_ref = Some(vk::AttachmentReference { + attachment: vk_attachments.len() as u32, + layout: ds.base.layout_in, + }); + let (load_op, store_op) = conv::map_attachment_ops(ds.base.ops); + let (stencil_load_op, stencil_store_op) = + conv::map_attachment_ops(ds.stencil_ops); + let vk_attachment = vk::AttachmentDescription::builder() + .format(ds.base.format) + .samples(samples) + .load_op(load_op) + .store_op(store_op) + .stencil_load_op(stencil_load_op) + .stencil_store_op(stencil_store_op) + .initial_layout(ds.base.layout_pre) + .final_layout(ds.base.layout_post) + .build(); + vk_attachments.push(vk_attachment); + } + + let vk_subpasses = [{ + let mut vk_subpass = vk::SubpassDescription::builder() + .pipeline_bind_point(vk::PipelineBindPoint::GRAPHICS) + .color_attachments(&color_refs) + .resolve_attachments(&resolve_refs); + if let Some(ref reference) = ds_ref { + vk_subpass = vk_subpass.depth_stencil_attachment(reference) + } + vk_subpass.build() + }]; + + let vk_info = vk::RenderPassCreateInfo::builder() + .attachments(&vk_attachments) + .subpasses(&vk_subpasses); + + let raw = unsafe { self.raw.create_render_pass(&vk_info, None)? }; + + *e.insert(raw) + } + }) + } + + pub fn make_framebuffer( + &self, + key: super::FramebufferKey, + raw_pass: vk::RenderPass, + pass_label: crate::Label, + ) -> Result { + Ok(match self.framebuffers.lock().entry(key) { + Entry::Occupied(e) => *e.get(), + Entry::Vacant(e) => { + let vk_views = e + .key() + .attachments + .iter() + .map(|at| at.raw) + .collect::>(); + let vk_view_formats = e + .key() + .attachments + .iter() + .map(|at| self.private_caps.map_texture_format(at.view_format)) + .collect::>(); + let vk_image_infos = e + .key() + .attachments + .iter() + .enumerate() + .map(|(i, at)| { + vk::FramebufferAttachmentImageInfo::builder() + .usage(conv::map_texture_usage(at.texture_usage)) + .flags(at.raw_image_flags) + .width(e.key().extent.width) + .height(e.key().extent.height) + .layer_count(e.key().extent.depth_or_array_layers) + .view_formats(&vk_view_formats[i..i + 1]) + .build() + }) + .collect::>(); + + let mut vk_attachment_info = vk::FramebufferAttachmentsCreateInfo::builder() + .attachment_image_infos(&vk_image_infos) + .build(); + let mut vk_info = vk::FramebufferCreateInfo::builder() + .render_pass(raw_pass) + .width(e.key().extent.width) + .height(e.key().extent.height) + .layers(e.key().extent.depth_or_array_layers); + + if self.private_caps.imageless_framebuffers { + //TODO: https://github.com/MaikKlein/ash/issues/450 + vk_info = vk_info + .flags(vk::FramebufferCreateFlags::IMAGELESS_KHR) + .push_next(&mut vk_attachment_info); + vk_info.attachment_count = e.key().attachments.len() as u32; + } else { + vk_info = vk_info.attachments(&vk_views); + } + + *e.insert(unsafe { + let raw = self.raw.create_framebuffer(&vk_info, None).unwrap(); + if let Some(label) = pass_label { + self.set_object_name(vk::ObjectType::FRAMEBUFFER, raw, label); + } + raw + }) + } + }) + } + + fn make_memory_ranges<'a, I: 'a + Iterator>( + &self, + buffer: &'a super::Buffer, + ranges: I, + ) -> impl 'a + Iterator { + let block = buffer.block.lock(); + let mask = self.private_caps.non_coherent_map_mask; + ranges.map(move |range| { + vk::MappedMemoryRange::builder() + .memory(*block.memory()) + .offset((block.offset() + range.start) & !mask) + .size((range.end - range.start + mask) & !mask) + .build() + }) + } + + unsafe fn free_resources(&self) { + for &raw in self.render_passes.lock().values() { + self.raw.destroy_render_pass(raw, None); + } + for &raw in self.framebuffers.lock().values() { + self.raw.destroy_framebuffer(raw, None); + } + self.raw.destroy_device(None); + } +} + +impl gpu_alloc::MemoryDevice for super::DeviceShared { + unsafe fn allocate_memory( + &self, + size: u64, + memory_type: u32, + flags: gpu_alloc::AllocationFlags, + ) -> Result { + let mut info = vk::MemoryAllocateInfo::builder() + .allocation_size(size) + .memory_type_index(memory_type); + + let mut info_flags; + + if flags.contains(gpu_alloc::AllocationFlags::DEVICE_ADDRESS) { + info_flags = vk::MemoryAllocateFlagsInfo::builder() + .flags(vk::MemoryAllocateFlags::DEVICE_ADDRESS); + info = info.push_next(&mut info_flags); + } + + match self.raw.allocate_memory(&info, None) { + Ok(memory) => Ok(memory), + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { + Err(gpu_alloc::OutOfMemory::OutOfDeviceMemory) + } + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { + Err(gpu_alloc::OutOfMemory::OutOfHostMemory) + } + Err(vk::Result::ERROR_TOO_MANY_OBJECTS) => panic!("Too many objects"), + Err(err) => panic!("Unexpected Vulkan error: `{}`", err), + } + } + + unsafe fn deallocate_memory(&self, memory: vk::DeviceMemory) { + self.raw.free_memory(memory, None); + } + + unsafe fn map_memory( + &self, + memory: &mut vk::DeviceMemory, + offset: u64, + size: u64, + ) -> Result, gpu_alloc::DeviceMapError> { + match self + .raw + .map_memory(*memory, offset, size, vk::MemoryMapFlags::empty()) + { + Ok(ptr) => Ok(ptr::NonNull::new(ptr as *mut u8) + .expect("Pointer to memory mapping must not be null")), + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { + Err(gpu_alloc::DeviceMapError::OutOfDeviceMemory) + } + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { + Err(gpu_alloc::DeviceMapError::OutOfHostMemory) + } + Err(vk::Result::ERROR_MEMORY_MAP_FAILED) => Err(gpu_alloc::DeviceMapError::MapFailed), + Err(err) => panic!("Unexpected Vulkan error: `{}`", err), + } + } + + unsafe fn unmap_memory(&self, memory: &mut vk::DeviceMemory) { + self.raw.unmap_memory(*memory); + } + + unsafe fn invalidate_memory_ranges( + &self, + _ranges: &[gpu_alloc::MappedMemoryRange<'_, vk::DeviceMemory>], + ) -> Result<(), gpu_alloc::OutOfMemory> { + // should never be called + unimplemented!() + } + + unsafe fn flush_memory_ranges( + &self, + _ranges: &[gpu_alloc::MappedMemoryRange<'_, vk::DeviceMemory>], + ) -> Result<(), gpu_alloc::OutOfMemory> { + // should never be called + unimplemented!() + } +} + +impl + gpu_descriptor::DescriptorDevice + for super::DeviceShared +{ + unsafe fn create_descriptor_pool( + &self, + descriptor_count: &gpu_descriptor::DescriptorTotalCount, + max_sets: u32, + flags: gpu_descriptor::DescriptorPoolCreateFlags, + ) -> Result { + //Note: ignoring other types, since they can't appear here + let unfiltered_counts = [ + (vk::DescriptorType::SAMPLER, descriptor_count.sampler), + ( + vk::DescriptorType::SAMPLED_IMAGE, + descriptor_count.sampled_image, + ), + ( + vk::DescriptorType::STORAGE_IMAGE, + descriptor_count.storage_image, + ), + ( + vk::DescriptorType::UNIFORM_BUFFER, + descriptor_count.uniform_buffer, + ), + ( + vk::DescriptorType::UNIFORM_BUFFER_DYNAMIC, + descriptor_count.uniform_buffer_dynamic, + ), + ( + vk::DescriptorType::STORAGE_BUFFER, + descriptor_count.storage_buffer, + ), + ( + vk::DescriptorType::STORAGE_BUFFER_DYNAMIC, + descriptor_count.storage_buffer_dynamic, + ), + ]; + + let filtered_counts = unfiltered_counts + .iter() + .cloned() + .filter(|&(_, count)| count != 0) + .map(|(ty, count)| vk::DescriptorPoolSize { + ty, + descriptor_count: count, + }) + .collect::>(); + + let mut vk_flags = vk::DescriptorPoolCreateFlags::empty(); + if flags.contains(gpu_descriptor::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET) { + vk_flags |= vk::DescriptorPoolCreateFlags::FREE_DESCRIPTOR_SET; + } + let vk_info = vk::DescriptorPoolCreateInfo::builder() + .max_sets(max_sets) + .flags(vk_flags) + .pool_sizes(&filtered_counts) + .build(); + + match self.raw.create_descriptor_pool(&vk_info, None) { + Ok(pool) => Ok(pool), + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) => { + Err(gpu_descriptor::CreatePoolError::OutOfHostMemory) + } + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { + Err(gpu_descriptor::CreatePoolError::OutOfDeviceMemory) + } + Err(vk::Result::ERROR_FRAGMENTATION) => { + Err(gpu_descriptor::CreatePoolError::Fragmentation) + } + Err(other) => { + log::error!("create_descriptor_pool: {:?}", other); + Err(gpu_descriptor::CreatePoolError::OutOfHostMemory) + } + } + } + + unsafe fn destroy_descriptor_pool(&self, pool: vk::DescriptorPool) { + self.raw.destroy_descriptor_pool(pool, None) + } + + unsafe fn alloc_descriptor_sets<'a>( + &self, + pool: &mut vk::DescriptorPool, + layouts: impl ExactSizeIterator, + sets: &mut impl Extend, + ) -> Result<(), gpu_descriptor::DeviceAllocationError> { + let result = inplace_or_alloc_from_iter(layouts.cloned(), |layouts_slice| { + let vk_info = vk::DescriptorSetAllocateInfo::builder() + .descriptor_pool(*pool) + .set_layouts(layouts_slice) + .build(); + self.raw.allocate_descriptor_sets(&vk_info) + }); + + match result { + Ok(vk_sets) => { + sets.extend(vk_sets); + Ok(()) + } + Err(vk::Result::ERROR_OUT_OF_HOST_MEMORY) + | Err(vk::Result::ERROR_OUT_OF_POOL_MEMORY) => { + Err(gpu_descriptor::DeviceAllocationError::OutOfHostMemory) + } + Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY) => { + Err(gpu_descriptor::DeviceAllocationError::OutOfDeviceMemory) + } + Err(vk::Result::ERROR_FRAGMENTED_POOL) => { + Err(gpu_descriptor::DeviceAllocationError::FragmentedPool) + } + Err(other) => { + log::error!("allocate_descriptor_sets: {:?}", other); + Err(gpu_descriptor::DeviceAllocationError::OutOfHostMemory) + } + } + } + + unsafe fn dealloc_descriptor_sets<'a>( + &self, + pool: &mut vk::DescriptorPool, + sets: impl Iterator, + ) { + let result = inplace_or_alloc_from_iter(sets, |sets_slice| { + self.raw.free_descriptor_sets(*pool, sets_slice) + }); + match result { + Ok(()) => {} + Err(err) => log::error!("free_descriptor_sets: {:?}", err), + } + } +} + +impl super::Device { + pub(super) unsafe fn create_swapchain( + &self, + surface: &mut super::Surface, + config: &crate::SurfaceConfiguration, + provided_old_swapchain: Option, + ) -> Result { + let functor = khr::Swapchain::new(&surface.instance.raw, &self.shared.raw); + + let old_swapchain = match provided_old_swapchain { + Some(osc) => osc.raw, + None => vk::SwapchainKHR::null(), + }; + + let info = vk::SwapchainCreateInfoKHR::builder() + .flags(vk::SwapchainCreateFlagsKHR::empty()) + .surface(surface.raw) + .min_image_count(config.swap_chain_size) + .image_format(self.shared.private_caps.map_texture_format(config.format)) + .image_color_space(vk::ColorSpaceKHR::SRGB_NONLINEAR) + .image_extent(vk::Extent2D { + width: config.extent.width, + height: config.extent.height, + }) + .image_array_layers(config.extent.depth_or_array_layers) + .image_usage(conv::map_texture_usage(config.usage)) + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .pre_transform(vk::SurfaceTransformFlagsKHR::IDENTITY) + .composite_alpha(conv::map_composite_alpha_mode(config.composite_alpha_mode)) + .present_mode(conv::map_present_mode(config.present_mode)) + .clipped(true) + .old_swapchain(old_swapchain); + + let result = functor.create_swapchain(&info, None); + + // doing this before bailing out with error + if old_swapchain != vk::SwapchainKHR::null() { + functor.destroy_swapchain(old_swapchain, None) + } + + let raw = match result { + Ok(swapchain) => swapchain, + Err(error) => { + return Err(match error { + vk::Result::ERROR_SURFACE_LOST_KHR => crate::SurfaceError::Lost, + vk::Result::ERROR_NATIVE_WINDOW_IN_USE_KHR => { + crate::SurfaceError::Other("Native window is in use") + } + other => crate::DeviceError::from(other).into(), + }) + } + }; + + let images = functor + .get_swapchain_images(raw) + .map_err(crate::DeviceError::from)?; + + let vk_info = vk::FenceCreateInfo::builder().build(); + let fence = self + .shared + .raw + .create_fence(&vk_info, None) + .map_err(crate::DeviceError::from)?; + + Ok(super::Swapchain { + raw, + functor, + device: Arc::clone(&self.shared), + fence, + images, + config: config.clone(), + }) + } +} + +impl crate::Device for super::Device { + unsafe fn exit(self) { + self.mem_allocator.into_inner().cleanup(&*self.shared); + self.desc_allocator.into_inner().cleanup(&*self.shared); + self.shared.free_resources(); + } + + unsafe fn create_buffer( + &self, + desc: &crate::BufferDescriptor, + ) -> Result { + let vk_info = vk::BufferCreateInfo::builder() + .size(desc.size) + .usage(conv::map_buffer_usage(desc.usage)) + .sharing_mode(vk::SharingMode::EXCLUSIVE); + + let raw = self.shared.raw.create_buffer(&vk_info, None)?; + let req = self.shared.raw.get_buffer_memory_requirements(raw); + + let mut alloc_usage = if desc + .usage + .intersects(crate::BufferUse::MAP_READ | crate::BufferUse::MAP_WRITE) + { + let mut flags = gpu_alloc::UsageFlags::HOST_ACCESS; + flags.set( + gpu_alloc::UsageFlags::DOWNLOAD, + desc.usage.contains(crate::BufferUse::MAP_READ), + ); + flags.set( + gpu_alloc::UsageFlags::UPLOAD, + desc.usage.contains(crate::BufferUse::MAP_WRITE), + ); + flags + } else { + gpu_alloc::UsageFlags::FAST_DEVICE_ACCESS + }; + alloc_usage.set( + gpu_alloc::UsageFlags::TRANSIENT, + desc.memory_flags.contains(crate::MemoryFlag::TRANSIENT), + ); + + let block = self.mem_allocator.lock().alloc( + &*self.shared, + gpu_alloc::Request { + size: req.size, + align_mask: req.alignment - 1, + usage: alloc_usage, + memory_types: req.memory_type_bits & self.valid_ash_memory_types, + }, + )?; + + self.shared + .raw + .bind_buffer_memory(raw, *block.memory(), block.offset())?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::BUFFER, raw, label); + } + + Ok(super::Buffer { + raw, + block: Mutex::new(block), + }) + } + unsafe fn destroy_buffer(&self, buffer: super::Buffer) { + self.shared.raw.destroy_buffer(buffer.raw, None); + self.mem_allocator + .lock() + .dealloc(&*self.shared, buffer.block.into_inner()); + } + + unsafe fn map_buffer( + &self, + buffer: &super::Buffer, + range: crate::MemoryRange, + ) -> Result { + let size = range.end - range.start; + let mut block = buffer.block.lock(); + let ptr = block.map(&*self.shared, range.start, size as usize)?; + let is_coherent = block + .props() + .contains(gpu_alloc::MemoryPropertyFlags::HOST_COHERENT); + Ok(crate::BufferMapping { ptr, is_coherent }) + } + unsafe fn unmap_buffer(&self, buffer: &super::Buffer) -> Result<(), crate::DeviceError> { + buffer.block.lock().unmap(&*self.shared); + Ok(()) + } + + unsafe fn flush_mapped_ranges(&self, buffer: &super::Buffer, ranges: I) + where + I: Iterator, + { + let vk_ranges = self.shared.make_memory_ranges(buffer, ranges); + inplace_or_alloc_from_iter(vk_ranges, |array| { + self.shared.raw.flush_mapped_memory_ranges(array).unwrap() + }); + } + unsafe fn invalidate_mapped_ranges(&self, buffer: &super::Buffer, ranges: I) + where + I: Iterator, + { + let vk_ranges = self.shared.make_memory_ranges(buffer, ranges); + inplace_or_alloc_from_iter(vk_ranges, |array| { + self.shared + .raw + .invalidate_mapped_memory_ranges(array) + .unwrap() + }); + } + + unsafe fn create_texture( + &self, + desc: &crate::TextureDescriptor, + ) -> Result { + let (array_layer_count, vk_extent) = conv::map_extent(desc.size, desc.dimension); + let mut raw_flags = vk::ImageCreateFlags::empty(); + if desc.dimension == wgt::TextureDimension::D2 && desc.size.depth_or_array_layers % 6 == 0 { + raw_flags |= vk::ImageCreateFlags::CUBE_COMPATIBLE; + } + + let vk_info = vk::ImageCreateInfo::builder() + .flags(raw_flags) + .image_type(conv::map_texture_dimension(desc.dimension)) + .format(self.shared.private_caps.map_texture_format(desc.format)) + .extent(vk_extent) + .mip_levels(desc.mip_level_count) + .array_layers(array_layer_count) + .samples(vk::SampleCountFlags::from_raw(desc.sample_count)) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(conv::map_texture_usage(desc.usage)) + .sharing_mode(vk::SharingMode::EXCLUSIVE) + .initial_layout(vk::ImageLayout::UNDEFINED); + + let raw = self.shared.raw.create_image(&vk_info, None)?; + let req = self.shared.raw.get_image_memory_requirements(raw); + + let block = self.mem_allocator.lock().alloc( + &*self.shared, + gpu_alloc::Request { + size: req.size, + align_mask: req.alignment - 1, + usage: gpu_alloc::UsageFlags::FAST_DEVICE_ACCESS, + memory_types: req.memory_type_bits & self.valid_ash_memory_types, + }, + )?; + + self.shared + .raw + .bind_image_memory(raw, *block.memory(), block.offset())?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::IMAGE, raw, label); + } + + Ok(super::Texture { + raw, + block: Some(block), + usage: desc.usage, + dim: desc.dimension, + aspects: crate::FormatAspect::from(desc.format), + format_info: desc.format.describe(), + sample_count: desc.sample_count, + size: desc.size, + raw_flags, + }) + } + unsafe fn destroy_texture(&self, texture: super::Texture) { + self.shared.raw.destroy_image(texture.raw, None); + self.mem_allocator + .lock() + .dealloc(&*self.shared, texture.block.unwrap()); + } + + unsafe fn create_texture_view( + &self, + texture: &super::Texture, + desc: &crate::TextureViewDescriptor, + ) -> Result { + let mut vk_info = vk::ImageViewCreateInfo::builder() + .flags(vk::ImageViewCreateFlags::empty()) + .image(texture.raw) + .view_type(conv::map_view_dimension(desc.dimension)) + .format(self.shared.private_caps.map_texture_format(desc.format)) + .subresource_range(conv::map_subresource_range(&desc.range, texture.aspects)); + + let mut image_view_info; + if self.shared.private_caps.image_view_usage { + image_view_info = vk::ImageViewUsageCreateInfo::builder() + .usage(conv::map_texture_usage(desc.usage)) + .build(); + vk_info = vk_info.push_next(&mut image_view_info); + } + + let raw = self.shared.raw.create_image_view(&vk_info, None)?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::IMAGE_VIEW, raw, label); + } + + let attachment = super::FramebufferAttachment { + raw: if self.shared.private_caps.imageless_framebuffers { + vk::ImageView::null() + } else { + raw + }, + texture_usage: texture.usage, + raw_image_flags: texture.raw_flags, + view_format: desc.format, + }; + let sample_count = texture.sample_count; + let render_size = wgt::Extent3d { + width: cmp::max(1, texture.size.width >> desc.range.base_mip_level), + height: cmp::max(1, texture.size.height >> desc.range.base_mip_level), + depth_or_array_layers: 1, + }; + + Ok(super::TextureView { + raw, + attachment, + sample_count, + render_size, + }) + } + unsafe fn destroy_texture_view(&self, view: super::TextureView) { + if !self.shared.private_caps.imageless_framebuffers { + let mut fbuf_lock = self.shared.framebuffers.lock(); + for (key, &raw_fbuf) in fbuf_lock.iter() { + if key.attachments.iter().any(|at| at.raw == view.raw) { + self.shared.raw.destroy_framebuffer(raw_fbuf, None); + } + } + fbuf_lock.retain(|key, _| !key.attachments.iter().any(|at| at.raw == view.raw)); + } + self.shared.raw.destroy_image_view(view.raw, None); + } + + unsafe fn create_sampler( + &self, + desc: &crate::SamplerDescriptor, + ) -> Result { + let mut vk_info = vk::SamplerCreateInfo::builder() + .flags(vk::SamplerCreateFlags::empty()) + .mag_filter(conv::map_filter_mode(desc.mag_filter)) + .min_filter(conv::map_filter_mode(desc.min_filter)) + .mipmap_mode(conv::map_mip_filter_mode(desc.mipmap_filter)) + .address_mode_u(conv::map_address_mode(desc.address_modes[0])) + .address_mode_v(conv::map_address_mode(desc.address_modes[1])) + .address_mode_w(conv::map_address_mode(desc.address_modes[2])); + + if let Some(fun) = desc.compare { + vk_info = vk_info + .compare_enable(true) + .compare_op(conv::map_comparison(fun)); + } + if let Some(ref range) = desc.lod_clamp { + vk_info = vk_info.min_lod(range.start).max_lod(range.end); + } + if let Some(aniso) = desc.anisotropy_clamp { + if self + .shared + .downlevel_flags + .contains(wgt::DownlevelFlags::ANISOTROPIC_FILTERING) + { + vk_info = vk_info + .anisotropy_enable(true) + .max_anisotropy(aniso.get() as f32); + } + } + if let Some(color) = desc.border_color { + vk_info = vk_info.border_color(conv::map_border_color(color)); + } + + let raw = self.shared.raw.create_sampler(&vk_info, None)?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::SAMPLER, raw, label); + } + + Ok(super::Sampler { raw }) + } + unsafe fn destroy_sampler(&self, sampler: super::Sampler) { + self.shared.raw.destroy_sampler(sampler.raw, None); + } + + unsafe fn create_command_encoder( + &self, + desc: &crate::CommandEncoderDescriptor, + ) -> Result { + let vk_info = vk::CommandPoolCreateInfo::builder() + .queue_family_index(desc.queue.family_index) + .build(); + let raw = self.shared.raw.create_command_pool(&vk_info, None)?; + + Ok(super::CommandEncoder { + raw, + device: Arc::clone(&self.shared), + active: vk::CommandBuffer::null(), + bind_point: vk::PipelineBindPoint::default(), + temp: super::Temp::default(), + free: Vec::new(), + discarded: Vec::new(), + }) + } + unsafe fn destroy_command_encoder(&self, cmd_encoder: super::CommandEncoder) { + if !cmd_encoder.free.is_empty() { + self.shared + .raw + .free_command_buffers(cmd_encoder.raw, &cmd_encoder.free); + } + if !cmd_encoder.discarded.is_empty() { + self.shared + .raw + .free_command_buffers(cmd_encoder.raw, &cmd_encoder.discarded); + } + self.shared.raw.destroy_command_pool(cmd_encoder.raw, None); + } + + unsafe fn create_bind_group_layout( + &self, + desc: &crate::BindGroupLayoutDescriptor, + ) -> Result { + let mut desc_count = gpu_descriptor::DescriptorTotalCount::default(); + let mut types = Vec::new(); + for entry in desc.entries { + let count = entry.count.map_or(1, |c| c.get()); + if entry.binding as usize >= types.len() { + types.resize( + entry.binding as usize + 1, + vk::DescriptorType::INPUT_ATTACHMENT, + ); + } + types[entry.binding as usize] = conv::map_binding_type(entry.ty); + + match entry.ty { + wgt::BindingType::Buffer { + ty, + has_dynamic_offset, + .. + } => match ty { + wgt::BufferBindingType::Uniform => { + if has_dynamic_offset { + desc_count.uniform_buffer_dynamic += count; + } else { + desc_count.uniform_buffer += count; + } + } + wgt::BufferBindingType::Storage { .. } => { + if has_dynamic_offset { + desc_count.storage_buffer_dynamic += count; + } else { + desc_count.storage_buffer += count; + } + } + }, + wgt::BindingType::Sampler { .. } => { + desc_count.sampler += count; + } + wgt::BindingType::Texture { .. } => { + desc_count.sampled_image += count; + } + wgt::BindingType::StorageTexture { .. } => { + desc_count.storage_image += count; + } + } + } + + //Note: not bothering with inplace_or_alloc_from_iter her as it's low frequency + let vk_bindings = desc + .entries + .iter() + .map(|entry| vk::DescriptorSetLayoutBinding { + binding: entry.binding, + descriptor_type: types[entry.binding as usize], + descriptor_count: entry.count.map_or(1, |c| c.get()), + stage_flags: conv::map_shader_stage(entry.visibility), + p_immutable_samplers: ptr::null(), + }) + .collect::>(); + + let vk_info = vk::DescriptorSetLayoutCreateInfo::builder() + .flags(vk::DescriptorSetLayoutCreateFlags::empty()) + .bindings(&vk_bindings); + + let raw = self + .shared + .raw + .create_descriptor_set_layout(&vk_info, None)?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::DESCRIPTOR_SET_LAYOUT, raw, label); + } + + Ok(super::BindGroupLayout { + raw, + desc_count, + types, + }) + } + unsafe fn destroy_bind_group_layout(&self, bg_layout: super::BindGroupLayout) { + self.shared + .raw + .destroy_descriptor_set_layout(bg_layout.raw, None); + } + + unsafe fn create_pipeline_layout( + &self, + desc: &crate::PipelineLayoutDescriptor, + ) -> Result { + //Note: not bothering with inplace_or_alloc_from_iter her as it's low frequency + let vk_set_layouts = desc + .bind_group_layouts + .iter() + .map(|bgl| bgl.raw) + .collect::>(); + let vk_push_constant_ranges = desc + .push_constant_ranges + .iter() + .map(|pcr| vk::PushConstantRange { + stage_flags: conv::map_shader_stage(pcr.stages), + offset: pcr.range.start, + size: pcr.range.end - pcr.range.start, + }) + .collect::>(); + + let vk_info = vk::PipelineLayoutCreateInfo::builder() + .flags(vk::PipelineLayoutCreateFlags::empty()) + .set_layouts(&vk_set_layouts) + .push_constant_ranges(&vk_push_constant_ranges); + + let raw = self.shared.raw.create_pipeline_layout(&vk_info, None)?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::PIPELINE_LAYOUT, raw, label); + } + + Ok(super::PipelineLayout { raw }) + } + unsafe fn destroy_pipeline_layout(&self, pipeline_layout: super::PipelineLayout) { + self.shared + .raw + .destroy_pipeline_layout(pipeline_layout.raw, None); + } + + unsafe fn create_bind_group( + &self, + desc: &crate::BindGroupDescriptor, + ) -> Result { + let mut vk_sets = self.desc_allocator.lock().allocate( + &*self.shared, + &desc.layout.raw, + gpu_descriptor::DescriptorSetLayoutCreateFlags::empty(), + &desc.layout.desc_count, + 1, + )?; + + let set = vk_sets.pop().unwrap(); + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::DESCRIPTOR_SET, *set.raw(), label); + } + + let mut writes = Vec::with_capacity(desc.entries.len()); + let mut buffer_infos = Vec::with_capacity(desc.buffers.len()); + let mut sampler_infos = Vec::with_capacity(desc.samplers.len()); + let mut image_infos = Vec::with_capacity(desc.textures.len()); + for entry in desc.entries { + let ty = desc.layout.types[entry.binding as usize]; + let mut write = vk::WriteDescriptorSet::builder() + .dst_set(*set.raw()) + .dst_binding(entry.binding) + .descriptor_type(ty); + write = match ty { + vk::DescriptorType::SAMPLER => { + let index = sampler_infos.len(); + let binding = desc.samplers[index]; + let vk_info = vk::DescriptorImageInfo::builder() + .sampler(binding.raw) + .build(); + sampler_infos.push(vk_info); + write.image_info(&sampler_infos[index..]) + } + vk::DescriptorType::SAMPLED_IMAGE | vk::DescriptorType::STORAGE_IMAGE => { + let index = image_infos.len(); + let binding = &desc.textures[index]; + let layout = conv::derive_image_layout(binding.usage, binding.view.aspects()); + let vk_info = vk::DescriptorImageInfo::builder() + .image_view(binding.view.raw) + .image_layout(layout) + .build(); + image_infos.push(vk_info); + write.image_info(&image_infos[index..]) + } + _ => { + let index = buffer_infos.len(); + let binding = &desc.buffers[index]; + let vk_info = vk::DescriptorBufferInfo::builder() + .buffer(binding.buffer.raw) + .offset(binding.offset) + .range(binding.size.map_or(vk::WHOLE_SIZE, wgt::BufferSize::get)) + .build(); + buffer_infos.push(vk_info); + write.buffer_info(&buffer_infos[index..]) + } + }; + writes.push(write.build()); + } + + self.shared.raw.update_descriptor_sets(&writes, &[]); + Ok(super::BindGroup { set }) + } + unsafe fn destroy_bind_group(&self, group: super::BindGroup) { + self.desc_allocator + .lock() + .free(&*self.shared, Some(group.set)); + } + + unsafe fn create_shader_module( + &self, + desc: &crate::ShaderModuleDescriptor, + shader: crate::NagaShader, + ) -> Result { + let spv = naga::back::spv::write_vec(&shader.module, &shader.info, &self.naga_options) + .map_err(|e| crate::ShaderError::Compilation(format!("{}", e)))?; + + let vk_info = vk::ShaderModuleCreateInfo::builder() + .flags(vk::ShaderModuleCreateFlags::empty()) + .code(&spv); + + let raw = self + .shared + .raw + .create_shader_module(&vk_info, None) + .map_err(crate::DeviceError::from)?; + + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::SHADER_MODULE, raw, label); + } + + Ok(super::ShaderModule { raw }) + } + unsafe fn destroy_shader_module(&self, module: super::ShaderModule) { + let _ = self.shared.raw.destroy_shader_module(module.raw, None); + } + + unsafe fn create_render_pipeline( + &self, + desc: &crate::RenderPipelineDescriptor, + ) -> Result { + let dynamic_states = [ + vk::DynamicState::VIEWPORT, + vk::DynamicState::SCISSOR, + vk::DynamicState::BLEND_CONSTANTS, + vk::DynamicState::STENCIL_REFERENCE, + ]; + let mut compatible_rp_key = super::RenderPassKey { + sample_count: desc.multisample.count, + ..Default::default() + }; + let mut stages = ArrayVec::<[_; 2]>::new(); + let mut vertex_buffers = Vec::with_capacity(desc.vertex_buffers.len()); + let mut vertex_attributes = Vec::new(); + + for (i, vb) in desc.vertex_buffers.iter().enumerate() { + vertex_buffers.push(vk::VertexInputBindingDescription { + binding: i as u32, + stride: vb.array_stride as u32, + input_rate: match vb.step_mode { + wgt::InputStepMode::Vertex => vk::VertexInputRate::VERTEX, + wgt::InputStepMode::Instance => vk::VertexInputRate::INSTANCE, + }, + }); + for at in vb.attributes { + vertex_attributes.push(vk::VertexInputAttributeDescription { + location: at.shader_location, + binding: i as u32, + format: conv::map_vertex_format(at.format), + offset: at.offset as u32, + }); + } + } + + let vk_vertex_input = vk::PipelineVertexInputStateCreateInfo::builder() + .vertex_binding_descriptions(&vertex_buffers) + .vertex_attribute_descriptions(&vertex_attributes) + .build(); + + let vk_input_assembly = vk::PipelineInputAssemblyStateCreateInfo::builder() + .topology(conv::map_topology(desc.primitive.topology)) + .primitive_restart_enable(desc.primitive.strip_index_format.is_some()) + .build(); + + let vs_entry_point; + { + let stage = &desc.vertex_stage; + vs_entry_point = CString::new(stage.entry_point).unwrap(); + stages.push( + vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::VERTEX) + .module(stage.module.raw) + .name(&vs_entry_point) + .build(), + ); + } + let fs_entry_point; + if let Some(ref stage) = desc.fragment_stage { + fs_entry_point = CString::new(stage.entry_point).unwrap(); + stages.push( + vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::FRAGMENT) + .module(stage.module.raw) + .name(&fs_entry_point) + .build(), + ); + } + + let mut vk_rasterization = vk::PipelineRasterizationStateCreateInfo::builder() + .depth_clamp_enable(desc.primitive.clamp_depth) + .polygon_mode(conv::map_polygon_mode(desc.primitive.polygon_mode)) + .front_face(conv::map_front_face(desc.primitive.front_face)) + .line_width(1.0); + if let Some(face) = desc.primitive.cull_mode { + vk_rasterization = vk_rasterization.cull_mode(conv::map_cull_face(face)) + } + + let mut vk_depth_stencil = vk::PipelineDepthStencilStateCreateInfo::builder(); + if let Some(ref ds) = desc.depth_stencil { + let vk_format = self.shared.private_caps.map_texture_format(ds.format); + let vk_layout = if ds.is_read_only() { + vk::ImageLayout::DEPTH_STENCIL_READ_ONLY_OPTIMAL + } else { + vk::ImageLayout::DEPTH_STENCIL_ATTACHMENT_OPTIMAL + }; + compatible_rp_key.depth_stencil = Some(super::DepthStencilAttachmentKey { + base: super::AttachmentKey::compatible(vk_format, vk_layout), + stencil_ops: crate::AttachmentOp::all(), + }); + + if ds.is_depth_enabled() { + vk_depth_stencil = vk_depth_stencil + .depth_test_enable(true) + .depth_write_enable(ds.depth_write_enabled) + .depth_compare_op(conv::map_comparison(ds.depth_compare)); + } + if ds.stencil.is_enabled() { + let front = conv::map_stencil_face(&ds.stencil.front); + let back = conv::map_stencil_face(&ds.stencil.back); + vk_depth_stencil = vk_depth_stencil + .stencil_test_enable(true) + .front(front) + .back(back); + } + + if ds.bias.is_enabled() { + vk_rasterization = vk_rasterization + .depth_bias_enable(true) + .depth_bias_constant_factor(ds.bias.constant as f32) + .depth_bias_clamp(ds.bias.clamp) + .depth_bias_slope_factor(ds.bias.slope_scale); + } + } + + let vk_viewport = vk::PipelineViewportStateCreateInfo::builder() + .flags(vk::PipelineViewportStateCreateFlags::empty()) + .scissor_count(1) + .viewport_count(1) + .build(); + + let vk_sample_mask = [ + desc.multisample.mask as u32, + (desc.multisample.mask >> 32) as u32, + ]; + let vk_multisample = vk::PipelineMultisampleStateCreateInfo::builder() + .rasterization_samples(vk::SampleCountFlags::from_raw(desc.multisample.count)) + .alpha_to_coverage_enable(desc.multisample.alpha_to_coverage_enabled) + .sample_mask(&vk_sample_mask) + .build(); + + let mut vk_attachments = Vec::with_capacity(desc.color_targets.len()); + for cat in desc.color_targets { + let vk_format = self.shared.private_caps.map_texture_format(cat.format); + compatible_rp_key.colors.push(super::ColorAttachmentKey { + base: super::AttachmentKey::compatible( + vk_format, + vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL, + ), + resolve: None, + }); + + let mut vk_attachment = vk::PipelineColorBlendAttachmentState::builder() + .color_write_mask(vk::ColorComponentFlags::from_raw(cat.write_mask.bits())); + if let Some(ref blend) = cat.blend { + let (color_op, color_src, color_dst) = conv::map_blend_component(&blend.color); + let (alpha_op, alpha_src, alpha_dst) = conv::map_blend_component(&blend.alpha); + vk_attachment = vk_attachment + .blend_enable(true) + .color_blend_op(color_op) + .src_color_blend_factor(color_src) + .dst_color_blend_factor(color_dst) + .alpha_blend_op(alpha_op) + .src_alpha_blend_factor(alpha_src) + .dst_alpha_blend_factor(alpha_dst); + } + vk_attachments.push(vk_attachment.build()); + } + + let vk_color_blend = vk::PipelineColorBlendStateCreateInfo::builder() + .attachments(&vk_attachments) + .build(); + + let vk_dynamic_state = vk::PipelineDynamicStateCreateInfo::builder() + .dynamic_states(&dynamic_states) + .build(); + + let raw_pass = self + .shared + .make_render_pass(compatible_rp_key) + .map_err(crate::DeviceError::from)?; + + let vk_infos = [{ + vk::GraphicsPipelineCreateInfo::builder() + .layout(desc.layout.raw) + .stages(&stages) + .vertex_input_state(&vk_vertex_input) + .input_assembly_state(&vk_input_assembly) + .rasterization_state(&vk_rasterization) + .viewport_state(&vk_viewport) + .multisample_state(&vk_multisample) + .depth_stencil_state(&vk_depth_stencil) + .color_blend_state(&vk_color_blend) + .dynamic_state(&vk_dynamic_state) + .render_pass(raw_pass) + .build() + }]; + + let mut raw_vec = self + .shared + .raw + .create_graphics_pipelines(vk::PipelineCache::null(), &vk_infos, None) + .map_err(|(_, e)| crate::DeviceError::from(e))?; + + let raw = raw_vec.pop().unwrap(); + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::PIPELINE, raw, label); + } + + Ok(super::RenderPipeline { raw }) + } + unsafe fn destroy_render_pipeline(&self, pipeline: super::RenderPipeline) { + self.shared.raw.destroy_pipeline(pipeline.raw, None); + } + + unsafe fn create_compute_pipeline( + &self, + desc: &crate::ComputePipelineDescriptor, + ) -> Result { + let cs_entry_point = CString::new(desc.stage.entry_point).unwrap(); + let vk_stage = vk::PipelineShaderStageCreateInfo::builder() + .stage(vk::ShaderStageFlags::COMPUTE) + .module(desc.stage.module.raw) + .name(&cs_entry_point) + .build(); + + let vk_infos = [{ + vk::ComputePipelineCreateInfo::builder() + .layout(desc.layout.raw) + .stage(vk_stage) + .build() + }]; + + let mut raw_vec = self + .shared + .raw + .create_compute_pipelines(vk::PipelineCache::null(), &vk_infos, None) + .map_err(|(_, e)| crate::DeviceError::from(e))?; + + let raw = raw_vec.pop().unwrap(); + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::PIPELINE, raw, label); + } + + Ok(super::ComputePipeline { raw }) + } + unsafe fn destroy_compute_pipeline(&self, pipeline: super::ComputePipeline) { + self.shared.raw.destroy_pipeline(pipeline.raw, None); + } + + unsafe fn create_query_set( + &self, + desc: &wgt::QuerySetDescriptor, + ) -> Result { + let (vk_type, pipeline_statistics) = match desc.ty { + wgt::QueryType::Occlusion => ( + vk::QueryType::OCCLUSION, + vk::QueryPipelineStatisticFlags::empty(), + ), + wgt::QueryType::PipelineStatistics(statistics) => ( + vk::QueryType::PIPELINE_STATISTICS, + conv::map_pipeline_statistics(statistics), + ), + wgt::QueryType::Timestamp => ( + vk::QueryType::TIMESTAMP, + vk::QueryPipelineStatisticFlags::empty(), + ), + }; + + let vk_info = vk::QueryPoolCreateInfo::builder() + .query_type(vk_type) + .query_count(desc.count) + .pipeline_statistics(pipeline_statistics) + .build(); + + let raw = self.shared.raw.create_query_pool(&vk_info, None)?; + if let Some(label) = desc.label { + self.shared + .set_object_name(vk::ObjectType::QUERY_POOL, raw, label); + } + + Ok(super::QuerySet { raw }) + } + unsafe fn destroy_query_set(&self, set: super::QuerySet) { + self.shared.raw.destroy_query_pool(set.raw, None); + } + unsafe fn create_fence(&self) -> Result { + Ok(super::Fence { + last_completed: 0, + active: Vec::new(), + free: Vec::new(), + }) + } + unsafe fn destroy_fence(&self, fence: super::Fence) { + for (_, raw) in fence.active { + self.shared.raw.destroy_fence(raw, None); + } + for raw in fence.free { + self.shared.raw.destroy_fence(raw, None); + } + } + unsafe fn get_fence_value( + &self, + fence: &super::Fence, + ) -> Result { + fence.get_latest(&self.shared.raw) + } + unsafe fn wait( + &self, + fence: &super::Fence, + wait_value: crate::FenceValue, + timeout_ms: u32, + ) -> Result { + if wait_value <= fence.last_completed { + Ok(true) + } else { + match fence.active.iter().find(|&&(value, _)| value >= wait_value) { + Some(&(_, raw)) => { + let timeout_us = timeout_ms as u64 * super::MILLIS_TO_NANOS; + match self.shared.raw.wait_for_fences(&[raw], true, timeout_us) { + Ok(()) => Ok(true), + Err(vk::Result::TIMEOUT) => Ok(false), + Err(other) => Err(other.into()), + } + } + None => { + log::error!("No signals reached value {}", wait_value); + Err(crate::DeviceError::Lost) + } + } + } + } + + unsafe fn start_capture(&self) -> bool { + false + } + unsafe fn stop_capture(&self) {} +} + +impl From for crate::DeviceError { + fn from(error: gpu_alloc::AllocationError) -> Self { + use gpu_alloc::AllocationError as Ae; + match error { + Ae::OutOfDeviceMemory | Ae::OutOfHostMemory => Self::OutOfMemory, + _ => { + log::error!("memory allocation: {:?}", error); + Self::Lost + } + } + } +} +impl From for crate::DeviceError { + fn from(error: gpu_alloc::MapError) -> Self { + use gpu_alloc::MapError as Me; + match error { + Me::OutOfDeviceMemory | Me::OutOfHostMemory => Self::OutOfMemory, + _ => { + log::error!("memory mapping: {:?}", error); + Self::Lost + } + } + } +} +impl From for crate::DeviceError { + fn from(error: gpu_descriptor::AllocationError) -> Self { + log::error!("descriptor allocation: {:?}", error); + Self::OutOfMemory + } +} diff --git a/wgpu-hal/src/vulkan/instance.rs b/wgpu-hal/src/vulkan/instance.rs new file mode 100644 index 0000000000..aa0708df8b --- /dev/null +++ b/wgpu-hal/src/vulkan/instance.rs @@ -0,0 +1,653 @@ +use std::{ + cmp, + ffi::{c_void, CStr, CString}, + mem, slice, + sync::Arc, + thread, +}; + +use ash::{ + extensions::{ext, khr}, + version::{DeviceV1_0 as _, EntryV1_0 as _, InstanceV1_0 as _}, + vk, +}; + +unsafe extern "system" fn debug_utils_messenger_callback( + message_severity: vk::DebugUtilsMessageSeverityFlagsEXT, + message_type: vk::DebugUtilsMessageTypeFlagsEXT, + callback_data_ptr: *const vk::DebugUtilsMessengerCallbackDataEXT, + _user_data: *mut c_void, +) -> vk::Bool32 { + use std::borrow::Cow; + if thread::panicking() { + return vk::FALSE; + } + + let message_severity = match message_severity { + vk::DebugUtilsMessageSeverityFlagsEXT::ERROR => log::Level::Error, + vk::DebugUtilsMessageSeverityFlagsEXT::WARNING => log::Level::Warn, + vk::DebugUtilsMessageSeverityFlagsEXT::INFO => log::Level::Info, + vk::DebugUtilsMessageSeverityFlagsEXT::VERBOSE => log::Level::Trace, + _ => log::Level::Warn, + }; + + let cd = &*callback_data_ptr; + + let message_id_name = if cd.p_message_id_name.is_null() { + Cow::from("") + } else { + CStr::from_ptr(cd.p_message_id_name).to_string_lossy() + }; + let message = if cd.p_message.is_null() { + Cow::from("") + } else { + CStr::from_ptr(cd.p_message).to_string_lossy() + }; + + log::log!( + message_severity, + "{:?} [{} (0x{:x})]\n\t{}", + message_type, + message_id_name, + cd.message_id_number, + message, + ); + + if cd.queue_label_count != 0 { + let labels = slice::from_raw_parts(cd.p_queue_labels, cd.queue_label_count as usize); + let names = labels + .iter() + .flat_map(|dul_obj| { + dul_obj + .p_label_name + .as_ref() + .map(|lbl| CStr::from_ptr(lbl).to_string_lossy()) + }) + .collect::>(); + log::log!(message_severity, "\tqueues: {}", names.join(", ")); + } + + if cd.cmd_buf_label_count != 0 { + let labels = slice::from_raw_parts(cd.p_cmd_buf_labels, cd.cmd_buf_label_count as usize); + let names = labels + .iter() + .flat_map(|dul_obj| { + dul_obj + .p_label_name + .as_ref() + .map(|lbl| CStr::from_ptr(lbl).to_string_lossy()) + }) + .collect::>(); + log::log!(message_severity, "\tcommand buffers: {}", names.join(", ")); + } + + if cd.object_count != 0 { + let labels = slice::from_raw_parts(cd.p_objects, cd.object_count as usize); + //TODO: use color fields of `vk::DebugUtilsLabelExt`? + let names = labels + .iter() + .map(|obj_info| { + let name = obj_info + .p_object_name + .as_ref() + .map(|name| CStr::from_ptr(name).to_string_lossy()) + .unwrap_or(Cow::Borrowed("?")); + + format!( + "(type: {:?}, hndl: 0x{:x}, name: {})", + obj_info.object_type, obj_info.object_handle, name + ) + }) + .collect::>(); + log::log!(message_severity, "\tobjects: {}", names.join(", ")); + } + + vk::FALSE +} + +impl super::Swapchain { + unsafe fn release_resources(self, device: &ash::Device) -> Self { + let _ = device.device_wait_idle(); + device.destroy_fence(self.fence, None); + self + } +} + +impl super::Instance { + #[allow(dead_code)] + fn create_surface_from_xlib( + &self, + dpy: *mut vk::Display, + window: vk::Window, + ) -> super::Surface { + if !self.extensions.contains(&khr::XlibSurface::name()) { + panic!("Vulkan driver does not support VK_KHR_XLIB_SURFACE"); + } + + let surface = { + let xlib_loader = khr::XlibSurface::new(&self.entry, &self.shared.raw); + let info = vk::XlibSurfaceCreateInfoKHR::builder() + .flags(vk::XlibSurfaceCreateFlagsKHR::empty()) + .window(window) + .dpy(dpy); + + unsafe { xlib_loader.create_xlib_surface(&info, None) } + .expect("XlibSurface::create_xlib_surface() failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[allow(dead_code)] + fn create_surface_from_xcb( + &self, + connection: *mut vk::xcb_connection_t, + window: vk::xcb_window_t, + ) -> super::Surface { + if !self.extensions.contains(&khr::XcbSurface::name()) { + panic!("Vulkan driver does not support VK_KHR_XCB_SURFACE"); + } + + let surface = { + let xcb_loader = khr::XcbSurface::new(&self.entry, &self.shared.raw); + let info = vk::XcbSurfaceCreateInfoKHR::builder() + .flags(vk::XcbSurfaceCreateFlagsKHR::empty()) + .window(window) + .connection(connection); + + unsafe { xcb_loader.create_xcb_surface(&info, None) } + .expect("XcbSurface::create_xcb_surface() failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[allow(dead_code)] + fn create_surface_from_wayland( + &self, + display: *mut c_void, + surface: *mut c_void, + ) -> super::Surface { + if !self.extensions.contains(&khr::WaylandSurface::name()) { + panic!("Vulkan driver does not support VK_KHR_WAYLAND_SURFACE"); + } + + let surface = { + let w_loader = khr::WaylandSurface::new(&self.entry, &self.shared.raw); + let info = vk::WaylandSurfaceCreateInfoKHR::builder() + .flags(vk::WaylandSurfaceCreateFlagsKHR::empty()) + .display(display) + .surface(surface); + + unsafe { w_loader.create_wayland_surface(&info, None) }.expect("WaylandSurface failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[allow(dead_code)] + fn create_surface_android(&self, window: *const c_void) -> super::Surface { + let surface = { + let a_loader = khr::AndroidSurface::new(&self.entry, &self.shared.raw); + let info = vk::AndroidSurfaceCreateInfoKHR::builder() + .flags(vk::AndroidSurfaceCreateFlagsKHR::empty()) + .window(window as *mut _); + + unsafe { a_loader.create_android_surface(&info, None) }.expect("AndroidSurface failed") + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[allow(dead_code)] + fn create_surface_from_hwnd( + &self, + hinstance: *mut c_void, + hwnd: *mut c_void, + ) -> super::Surface { + if !self.extensions.contains(&khr::Win32Surface::name()) { + panic!("Vulkan driver does not support VK_KHR_WIN32_SURFACE"); + } + + let surface = { + let info = vk::Win32SurfaceCreateInfoKHR::builder() + .flags(vk::Win32SurfaceCreateFlagsKHR::empty()) + .hinstance(hinstance) + .hwnd(hwnd); + let win32_loader = khr::Win32Surface::new(&self.entry, &self.shared.raw); + unsafe { + win32_loader + .create_win32_surface(&info, None) + .expect("Unable to create Win32 surface") + } + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + fn create_surface_from_ns_view(&self, view: *mut c_void) -> super::Surface { + use core_graphics_types::{base::CGFloat, geometry::CGRect}; + use objc::{ + class, msg_send, + runtime::{Object, BOOL, YES}, + sel, sel_impl, + }; + + let layer = unsafe { + let view = view as *mut Object; + let existing: *mut Object = msg_send![view, layer]; + let class = class!(CAMetalLayer); + + let use_current = if existing.is_null() { + false + } else { + let result: BOOL = msg_send![existing, isKindOfClass: class]; + result == YES + }; + + if use_current { + existing + } else { + let layer: *mut Object = msg_send![class, new]; + let () = msg_send![view, setLayer: layer]; + let bounds: CGRect = msg_send![view, bounds]; + let () = msg_send![layer, setBounds: bounds]; + + let window: *mut Object = msg_send![view, window]; + if !window.is_null() { + let scale_factor: CGFloat = msg_send![window, backingScaleFactor]; + let () = msg_send![layer, setContentsScale: scale_factor]; + } + layer + } + }; + + let surface = { + let metal_loader = ext::MetalSurface::new(&self.entry, &self.shared.raw); + let vk_info = vk::MetalSurfaceCreateInfoEXT::builder() + .flags(vk::MetalSurfaceCreateFlagsEXT::empty()) + .layer(layer as *mut _) + .build(); + + unsafe { metal_loader.create_metal_surface(&vk_info, None).unwrap() } + }; + + self.create_surface_from_vk_surface_khr(surface) + } + + fn create_surface_from_vk_surface_khr(&self, surface: vk::SurfaceKHR) -> super::Surface { + let functor = khr::Surface::new(&self.entry, &self.shared.raw); + super::Surface { + raw: surface, + functor, + instance: Arc::clone(&self.shared), + swapchain: None, + } + } +} + +impl crate::Instance for super::Instance { + unsafe fn init(desc: &crate::InstanceDescriptor) -> Result { + let entry = match ash::Entry::new() { + Ok(entry) => entry, + Err(err) => { + log::info!("Missing Vulkan entry points: {:?}", err); + return Err(crate::InstanceError); + } + }; + let driver_api_version = match entry.try_enumerate_instance_version() { + // Vulkan 1.1+ + Ok(Some(version)) => version, + Ok(None) => vk::API_VERSION_1_0, + Err(err) => { + log::warn!("try_enumerate_instance_version: {:?}", err); + return Err(crate::InstanceError); + } + }; + + let app_name = CString::new(desc.name).unwrap(); + let app_info = vk::ApplicationInfo::builder() + .application_name(app_name.as_c_str()) + .application_version(1) + .engine_name(CStr::from_bytes_with_nul(b"wgpu-hal\0").unwrap()) + .engine_version(2) + .api_version({ + // Pick the latest API version available, but don't go later than the SDK version used by `gfx_backend_vulkan`. + cmp::min(driver_api_version, { + // This is the max Vulkan API version supported by `wgpu-hal`. + // + // If we want to increment this, there are some things that must be done first: + // - Audit the behavioral differences between the previous and new API versions. + // - Audit all extensions used by this backend: + // - If any were promoted in the new API version and the behavior has changed, we must handle the new behavior in addition to the old behavior. + // - If any were obsoleted in the new API version, we must implement a fallback for the new API version + // - If any are non-KHR-vendored, we must ensure the new behavior is still correct (since backwards-compatibility is not guaranteed). + vk::HEADER_VERSION_COMPLETE + }) + }); + + let instance_extensions = entry + .enumerate_instance_extension_properties() + .map_err(|e| { + log::info!("enumerate_instance_extension_properties: {:?}", e); + crate::InstanceError + })?; + + let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| { + log::info!("enumerate_instance_layer_properties: {:?}", e); + crate::InstanceError + })?; + + // Check our extensions against the available extensions + let extensions = { + let mut extensions: Vec<&'static CStr> = Vec::new(); + extensions.push(khr::Surface::name()); + + // Platform-specific WSI extensions + if cfg!(all( + unix, + not(target_os = "android"), + not(target_os = "macos") + )) { + extensions.push(khr::XlibSurface::name()); + extensions.push(khr::XcbSurface::name()); + extensions.push(khr::WaylandSurface::name()); + } + if cfg!(target_os = "android") { + extensions.push(khr::AndroidSurface::name()); + } + if cfg!(target_os = "windows") { + extensions.push(khr::Win32Surface::name()); + } + if cfg!(target_os = "macos") { + extensions.push(ext::MetalSurface::name()); + } + + if desc.flags.contains(crate::InstanceFlag::DEBUG) { + extensions.push(ext::DebugUtils::name()); + } + + extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name()); + + // VK_KHR_storage_buffer_storage_class required for `Naga` on Vulkan 1.0 devices + if driver_api_version == vk::API_VERSION_1_0 { + extensions.push(vk::KhrStorageBufferStorageClassFn::name()); + } + + // Only keep available extensions. + extensions.retain(|&ext| { + if instance_extensions + .iter() + .any(|inst_ext| CStr::from_ptr(inst_ext.extension_name.as_ptr()) == ext) + { + true + } else { + log::info!("Unable to find extension: {}", ext.to_string_lossy()); + false + } + }); + extensions + }; + + if driver_api_version == vk::API_VERSION_1_0 + && !extensions.contains(&vk::KhrStorageBufferStorageClassFn::name()) + { + log::warn!("Required VK_KHR_storage_buffer_storage_class extension is not supported"); + return Err(crate::InstanceError); + } + + // Check requested layers against the available layers + let layers = { + let mut layers: Vec<&'static CStr> = Vec::new(); + if desc.flags.contains(crate::InstanceFlag::VALIDATION) { + layers.push(CStr::from_bytes_with_nul(b"VK_LAYER_KHRONOS_validation\0").unwrap()); + } + + // Only keep available layers. + layers.retain(|&layer| { + if instance_layers + .iter() + .any(|inst_layer| CStr::from_ptr(inst_layer.layer_name.as_ptr()) == layer) + { + true + } else { + log::warn!("Unable to find layer: {}", layer.to_string_lossy()); + false + } + }); + layers + }; + + let vk_instance = { + let str_pointers = layers + .iter() + .chain(extensions.iter()) + .map(|&s| { + // Safe because `layers` and `extensions` entries have static lifetime. + s.as_ptr() + }) + .collect::>(); + + let create_info = vk::InstanceCreateInfo::builder() + .flags(vk::InstanceCreateFlags::empty()) + .application_info(&app_info) + .enabled_layer_names(&str_pointers[..layers.len()]) + .enabled_extension_names(&str_pointers[layers.len()..]); + + entry.create_instance(&create_info, None).map_err(|e| { + log::warn!("create_instance: {:?}", e); + crate::InstanceError + })? + }; + + let debug_utils = if extensions.contains(&ext::DebugUtils::name()) { + let extension = ext::DebugUtils::new(&entry, &vk_instance); + let vk_info = vk::DebugUtilsMessengerCreateInfoEXT::builder() + .flags(vk::DebugUtilsMessengerCreateFlagsEXT::empty()) + .message_severity(vk::DebugUtilsMessageSeverityFlagsEXT::all()) + .message_type(vk::DebugUtilsMessageTypeFlagsEXT::all()) + .pfn_user_callback(Some(debug_utils_messenger_callback)); + let messenger = extension + .create_debug_utils_messenger(&vk_info, None) + .unwrap(); + Some(super::DebugUtils { + extension, + messenger, + }) + } else { + None + }; + + let get_physical_device_properties = extensions + .iter() + .find(|&&ext| ext == vk::KhrGetPhysicalDeviceProperties2Fn::name()) + .map(|_| { + vk::KhrGetPhysicalDeviceProperties2Fn::load(|name| { + mem::transmute( + entry.get_instance_proc_addr(vk_instance.handle(), name.as_ptr()), + ) + }) + }); + + Ok(Self { + shared: Arc::new(super::InstanceShared { + raw: vk_instance, + flags: desc.flags, + debug_utils, + get_physical_device_properties, + }), + extensions, + entry, + }) + } + + unsafe fn create_surface( + &self, + has_handle: &impl raw_window_handle::HasRawWindowHandle, + ) -> Result { + use raw_window_handle::RawWindowHandle; + + match has_handle.raw_window_handle() { + #[cfg(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "solaris") + ))] + RawWindowHandle::Wayland(handle) + if self.extensions.contains(&khr::WaylandSurface::name()) => + { + Ok(self.create_surface_from_wayland(handle.display, handle.surface)) + } + #[cfg(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "ios"), + not(target_os = "solaris") + ))] + RawWindowHandle::Xlib(handle) + if self.extensions.contains(&khr::XlibSurface::name()) => + { + Ok(self.create_surface_from_xlib(handle.display as *mut _, handle.window)) + } + #[cfg(all( + unix, + not(target_os = "android"), + not(target_os = "macos"), + not(target_os = "ios") + ))] + RawWindowHandle::Xcb(handle) if self.extensions.contains(&khr::XcbSurface::name()) => { + Ok(self.create_surface_from_xcb(handle.connection, handle.window)) + } + #[cfg(target_os = "android")] + RawWindowHandle::Android(handle) => { + Ok(self.create_surface_android(handle.a_native_window)) + } + #[cfg(windows)] + RawWindowHandle::Windows(handle) => { + use winapi::um::libloaderapi::GetModuleHandleW; + + let hinstance = GetModuleHandleW(std::ptr::null()); + Ok(self.create_surface_from_hwnd(hinstance as *mut _, handle.hwnd)) + } + #[cfg(target_os = "macos")] + RawWindowHandle::MacOS(handle) + if self.extensions.contains(&ext::MetalSurface::name()) => + { + Ok(self.create_surface_from_ns_view(handle.ns_view)) + } + _ => Err(crate::InstanceError), + } + } + + unsafe fn destroy_surface(&self, surface: super::Surface) { + surface.functor.destroy_surface(surface.raw, None); + } + + unsafe fn enumerate_adapters(&self) -> Vec> { + let raw_devices = match self.shared.raw.enumerate_physical_devices() { + Ok(devices) => devices, + Err(err) => { + log::error!("enumerate_adapters: {}", err); + Vec::new() + } + }; + + raw_devices + .into_iter() + .flat_map(|device| self.expose_adapter(device)) + .collect() + } +} + +impl crate::Surface for super::Surface { + unsafe fn configure( + &mut self, + device: &super::Device, + config: &crate::SurfaceConfiguration, + ) -> Result<(), crate::SurfaceError> { + let old = self + .swapchain + .take() + .map(|sc| sc.release_resources(&device.shared.raw)); + + let swapchain = device.create_swapchain(self, config, old)?; + self.swapchain = Some(swapchain); + + Ok(()) + } + + unsafe fn unconfigure(&mut self, device: &super::Device) { + if let Some(sc) = self.swapchain.take() { + let swapchain = sc.release_resources(&device.shared.raw); + swapchain.functor.destroy_swapchain(swapchain.raw, None); + } + } + + unsafe fn acquire_texture( + &mut self, + timeout_ms: u32, + ) -> Result>, crate::SurfaceError> { + let sc = self.swapchain.as_mut().unwrap(); + let timeout_ns = timeout_ms as u64 * super::MILLIS_TO_NANOS; + + // will block if no image is available + let (index, suboptimal) = + match sc + .functor + .acquire_next_image(sc.raw, timeout_ns, vk::Semaphore::null(), sc.fence) + { + Ok(pair) => pair, + Err(error) => { + return match error { + vk::Result::TIMEOUT => Ok(None), + vk::Result::NOT_READY | vk::Result::ERROR_OUT_OF_DATE_KHR => { + Err(crate::SurfaceError::Outdated) + } + vk::Result::ERROR_SURFACE_LOST_KHR => Err(crate::SurfaceError::Lost), + other => Err(crate::DeviceError::from(other).into()), + } + } + }; + + // special case for Intel Vulkan returning bizzare values (ugh) + if sc.device.vendor_id == crate::util::db::intel::VENDOR && index > 0x100 { + return Err(crate::SurfaceError::Outdated); + } + + let fences = &[sc.fence]; + + sc.device + .raw + .wait_for_fences(fences, true, !0) + .map_err(crate::DeviceError::from)?; + sc.device + .raw + .reset_fences(fences) + .map_err(crate::DeviceError::from)?; + + let texture = super::SurfaceTexture { + index, + texture: super::Texture { + raw: sc.images[index as usize], + block: None, + usage: sc.config.usage, + dim: wgt::TextureDimension::D2, + aspects: crate::FormatAspect::COLOR, + format_info: sc.config.format.describe(), + sample_count: 1, + size: sc.config.extent, + raw_flags: vk::ImageCreateFlags::empty(), + }, + }; + Ok(Some(crate::AcquiredSurfaceTexture { + texture, + suboptimal, + })) + } + + unsafe fn discard_texture(&mut self, _texture: super::SurfaceTexture) {} +} diff --git a/wgpu-hal/src/vulkan/mod.rs b/wgpu-hal/src/vulkan/mod.rs new file mode 100644 index 0000000000..59133f09ab --- /dev/null +++ b/wgpu-hal/src/vulkan/mod.rs @@ -0,0 +1,465 @@ +mod adapter; +mod command; +mod conv; +mod device; +mod instance; + +use std::{borrow::Borrow, ffi::CStr, sync::Arc}; + +use arrayvec::ArrayVec; +use ash::{ + extensions::{ext, khr}, + version::DeviceV1_0, + vk, +}; +use parking_lot::Mutex; + +const MILLIS_TO_NANOS: u64 = 1_000_000; +const MAX_TOTAL_ATTACHMENTS: usize = crate::MAX_COLOR_TARGETS * 2 + 1; + +#[derive(Clone)] +pub struct Api; + +impl crate::Api for Api { + type Instance = Instance; + type Surface = Surface; + type Adapter = Adapter; + type Device = Device; + + type Queue = Queue; + type CommandEncoder = CommandEncoder; + type CommandBuffer = CommandBuffer; + + type Buffer = Buffer; + type Texture = Texture; + type SurfaceTexture = SurfaceTexture; + type TextureView = TextureView; + type Sampler = Sampler; + type QuerySet = QuerySet; + type Fence = Fence; + + type BindGroupLayout = BindGroupLayout; + type BindGroup = BindGroup; + type PipelineLayout = PipelineLayout; + type ShaderModule = ShaderModule; + type RenderPipeline = RenderPipeline; + type ComputePipeline = ComputePipeline; +} + +struct DebugUtils { + extension: ext::DebugUtils, + #[allow(dead_code)] + messenger: vk::DebugUtilsMessengerEXT, +} + +struct InstanceShared { + raw: ash::Instance, + flags: crate::InstanceFlag, + debug_utils: Option, + get_physical_device_properties: Option, +} + +pub struct Instance { + shared: Arc, + extensions: Vec<&'static CStr>, + entry: ash::Entry, +} + +struct Swapchain { + raw: vk::SwapchainKHR, + functor: khr::Swapchain, + device: Arc, + fence: vk::Fence, + images: Vec, + config: crate::SurfaceConfiguration, +} + +pub struct Surface { + raw: vk::SurfaceKHR, + functor: khr::Surface, + instance: Arc, + swapchain: Option, +} + +#[derive(Debug)] +pub struct SurfaceTexture { + index: u32, + texture: Texture, +} + +impl Borrow for SurfaceTexture { + fn borrow(&self) -> &Texture { + &self.texture + } +} + +pub struct Adapter { + raw: vk::PhysicalDevice, + instance: Arc, + //queue_families: Vec, + known_memory_flags: vk::MemoryPropertyFlags, + phd_capabilities: adapter::PhysicalDeviceCapabilities, + //phd_features: adapter::PhysicalDeviceFeatures, + downlevel_flags: wgt::DownlevelFlags, + private_caps: PrivateCapabilities, +} + +// TODO there's no reason why this can't be unified--the function pointers should all be the same--it's not clear how to do this with `ash`. +enum ExtensionFn { + /// The loaded function pointer struct for an extension. + Extension(T), + /// The extension was promoted to a core version of Vulkan and the functions on `ash`'s `DeviceV1_x` traits should be used. + Promoted, +} + +struct DeviceExtensionFunctions { + draw_indirect_count: Option>, +} + +/// Set of internal capabilities, which don't show up in the exposed +/// device geometry, but affect the code paths taken internally. +#[derive(Clone, Debug)] +struct PrivateCapabilities { + /// Y-flipping is implemented with either `VK_AMD_negative_viewport_height` or `VK_KHR_maintenance1`/1.1+. The AMD extension for negative viewport height does not require a Y shift. + /// + /// This flag is `true` if the device has `VK_KHR_maintenance1`/1.1+ and `false` otherwise (i.e. in the case of `VK_AMD_negative_viewport_height`). + flip_y_requires_shift: bool, + imageless_framebuffers: bool, + image_view_usage: bool, + texture_d24: bool, + texture_d24_s8: bool, + non_coherent_map_mask: wgt::BufferAddress, +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +struct AttachmentKey { + format: vk::Format, + layout_pre: vk::ImageLayout, + layout_in: vk::ImageLayout, + layout_post: vk::ImageLayout, + ops: crate::AttachmentOp, +} + +impl AttachmentKey { + /// Returns an attachment key for a compatible attachment. + fn compatible(format: vk::Format, layout_in: vk::ImageLayout) -> Self { + Self { + format, + layout_pre: vk::ImageLayout::GENERAL, + layout_in, + layout_post: vk::ImageLayout::GENERAL, + ops: crate::AttachmentOp::all(), + } + } +} + +#[derive(Clone, Eq, Hash, PartialEq)] +struct ColorAttachmentKey { + base: AttachmentKey, + resolve: Option, +} + +#[derive(Clone, Eq, Hash, PartialEq)] +struct DepthStencilAttachmentKey { + base: AttachmentKey, + stencil_ops: crate::AttachmentOp, +} + +#[derive(Clone, Eq, Default, Hash, PartialEq)] +struct RenderPassKey { + colors: ArrayVec<[ColorAttachmentKey; crate::MAX_COLOR_TARGETS]>, + depth_stencil: Option, + sample_count: u32, +} + +#[derive(Clone, Debug, Eq, Hash, PartialEq)] +struct FramebufferAttachment { + /// Can be NULL if the framebuffer is image-less + raw: vk::ImageView, + texture_usage: crate::TextureUse, + raw_image_flags: vk::ImageCreateFlags, + view_format: wgt::TextureFormat, +} + +#[derive(Clone, Eq, Default, Hash, PartialEq)] +struct FramebufferKey { + attachments: ArrayVec<[FramebufferAttachment; MAX_TOTAL_ATTACHMENTS]>, + extent: wgt::Extent3d, + sample_count: u32, +} + +impl FramebufferKey { + fn add(&mut self, view: &TextureView) { + self.extent.width = self.extent.width.max(view.render_size.width); + self.extent.height = self.extent.height.max(view.render_size.height); + self.extent.depth_or_array_layers = self + .extent + .depth_or_array_layers + .max(view.render_size.depth_or_array_layers); + self.sample_count = self.sample_count.max(view.sample_count); + self.attachments.push(view.attachment.clone()); + } +} + +struct DeviceShared { + raw: ash::Device, + instance: Arc, + extension_fns: DeviceExtensionFunctions, + vendor_id: u32, + _timestamp_period: f32, + downlevel_flags: wgt::DownlevelFlags, + private_caps: PrivateCapabilities, + render_passes: Mutex>, + framebuffers: Mutex>, +} + +pub struct Device { + shared: Arc, + mem_allocator: Mutex>, + desc_allocator: + Mutex>, + valid_ash_memory_types: u32, + naga_options: naga::back::spv::Options, +} + +pub struct Queue { + raw: vk::Queue, + swapchain_fn: khr::Swapchain, + device: Arc, + family_index: u32, +} + +#[derive(Debug)] +pub struct Buffer { + raw: vk::Buffer, + block: Mutex>, +} + +#[derive(Debug)] +pub struct Texture { + raw: vk::Image, + block: Option>, + usage: crate::TextureUse, + dim: wgt::TextureDimension, + aspects: crate::FormatAspect, + format_info: wgt::TextureFormatInfo, + sample_count: u32, + size: wgt::Extent3d, + raw_flags: vk::ImageCreateFlags, +} + +#[derive(Debug)] +pub struct TextureView { + raw: vk::ImageView, + attachment: FramebufferAttachment, + sample_count: u32, + render_size: wgt::Extent3d, +} + +impl TextureView { + fn aspects(&self) -> crate::FormatAspect { + self.attachment.view_format.into() + } +} + +#[derive(Debug)] +pub struct Sampler { + raw: vk::Sampler, +} + +#[derive(Debug)] +pub struct BindGroupLayout { + raw: vk::DescriptorSetLayout, + desc_count: gpu_descriptor::DescriptorTotalCount, + types: Vec, +} + +#[derive(Debug)] +pub struct PipelineLayout { + raw: vk::PipelineLayout, +} + +#[derive(Debug)] +pub struct BindGroup { + set: gpu_descriptor::DescriptorSet, +} + +#[derive(Default)] +struct Temp { + marker: Vec, + buffer_barriers: Vec, + image_barriers: Vec, +} + +unsafe impl Send for Temp {} +unsafe impl Sync for Temp {} + +impl Temp { + fn clear(&mut self) { + self.marker.clear(); + self.buffer_barriers.clear(); + self.image_barriers.clear(); + //see also - https://github.com/NotIntMan/inplace_it/issues/8 + } + + fn make_c_str(&mut self, name: &str) -> &CStr { + self.marker.clear(); + self.marker.extend_from_slice(name.as_bytes()); + self.marker.push(0); + unsafe { CStr::from_bytes_with_nul_unchecked(&self.marker) } + } +} + +pub struct CommandEncoder { + raw: vk::CommandPool, + device: Arc, + active: vk::CommandBuffer, + bind_point: vk::PipelineBindPoint, + temp: Temp, + free: Vec, + discarded: Vec, +} + +pub struct CommandBuffer { + raw: vk::CommandBuffer, +} + +#[derive(Debug)] +pub struct ShaderModule { + raw: vk::ShaderModule, +} + +#[derive(Debug)] +pub struct RenderPipeline { + raw: vk::Pipeline, +} + +#[derive(Debug)] +pub struct ComputePipeline { + raw: vk::Pipeline, +} + +#[derive(Debug)] +pub struct QuerySet { + raw: vk::QueryPool, +} + +#[derive(Debug)] +pub struct Fence { + last_completed: crate::FenceValue, + /// The pending fence values have to be ascending. + active: Vec<(crate::FenceValue, vk::Fence)>, + free: Vec, +} + +impl Fence { + fn get_latest(&self, device: &ash::Device) -> Result { + let mut max_value = self.last_completed; + for &(value, raw) in self.active.iter() { + unsafe { + if value > max_value && device.get_fence_status(raw)? { + max_value = value; + } + } + } + Ok(max_value) + } + + fn maintain(&mut self, device: &ash::Device) -> Result<(), crate::DeviceError> { + let latest = self.get_latest(device)?; + let base_free = self.free.len(); + for &(value, raw) in self.active.iter() { + if value <= latest { + self.free.push(raw); + } + } + if self.free.len() != base_free { + self.active.retain(|&(value, _)| value > latest); + unsafe { + device.reset_fences(&self.free[base_free..])?; + } + } + Ok(()) + } +} + +impl crate::Queue for Queue { + unsafe fn submit( + &mut self, + command_buffers: &[&CommandBuffer], + signal_fence: Option<(&mut Fence, crate::FenceValue)>, + ) -> Result<(), crate::DeviceError> { + let fence_raw = match signal_fence { + Some((fence, value)) => { + fence.maintain(&self.device.raw)?; + let raw = match fence.free.pop() { + Some(raw) => raw, + None => { + let vk_info = vk::FenceCreateInfo::builder().build(); + self.device.raw.create_fence(&vk_info, None)? + } + }; + fence.active.push((value, raw)); + raw + } + None => vk::Fence::null(), + }; + + let vk_cmd_buffers = command_buffers + .iter() + .map(|cmd| cmd.raw) + .collect::>(); + //TODO: semaphores + + let vk_info = vk::SubmitInfo::builder() + .command_buffers(&vk_cmd_buffers) + .build(); + + self.device + .raw + .queue_submit(self.raw, &[vk_info], fence_raw)?; + Ok(()) + } + + unsafe fn present( + &mut self, + surface: &mut Surface, + texture: SurfaceTexture, + ) -> Result<(), crate::SurfaceError> { + let ssc = surface.swapchain.as_ref().unwrap(); + + let swapchains = [ssc.raw]; + let image_indices = [texture.index]; + let vk_info = vk::PresentInfoKHR::builder() + .swapchains(&swapchains) + .image_indices(&image_indices); + + let suboptimal = self + .swapchain_fn + .queue_present(self.raw, &vk_info) + .map_err(|error| match error { + vk::Result::ERROR_OUT_OF_DATE_KHR => crate::SurfaceError::Outdated, + vk::Result::ERROR_SURFACE_LOST_KHR => crate::SurfaceError::Lost, + _ => crate::DeviceError::from(error).into(), + })?; + if suboptimal { + log::warn!("Suboptimal present of frame {}", texture.index); + } + Ok(()) + } +} + +impl From for crate::DeviceError { + fn from(result: vk::Result) -> Self { + match result { + vk::Result::ERROR_OUT_OF_HOST_MEMORY | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY => { + Self::OutOfMemory + } + vk::Result::ERROR_DEVICE_LOST => Self::Lost, + _ => { + log::warn!("Unrecognized device error {:?}", result); + Self::Lost + } + } + } +} diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 8e6bd4c3b9..9c51c43b40 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -152,6 +152,8 @@ impl Default for RequestAdapterOptions { } } +//TODO: make robust resource access configurable + bitflags::bitflags! { /// Features that are not guaranteed to be supported. /// @@ -650,16 +652,14 @@ impl Default for Limits { /// Lists various ways the underlying platform does not conform to the WebGPU standard. #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct DownlevelProperties { +pub struct DownlevelCapabilities { /// Combined boolean flags. pub flags: DownlevelFlags, /// Which collections of features shaders support. Defined in terms of D3D's shader models. pub shader_model: ShaderModel, } -impl Default for DownlevelProperties { - // Note, this defaults to all on, as that is the default assumption in wgpu. - // gfx-hal's equivalent structure defaults to all off. +impl Default for DownlevelCapabilities { fn default() -> Self { Self { flags: DownlevelFlags::COMPLIANT, @@ -668,7 +668,7 @@ impl Default for DownlevelProperties { } } -impl DownlevelProperties { +impl DownlevelCapabilities { /// Returns true if the underlying platform offers complete support of the baseline WebGPU standard. /// /// If this returns false, some parts of the API will result in validation errors where they would not normally. @@ -695,10 +695,12 @@ bitflags::bitflags! { const NON_POWER_OF_TWO_MIPMAPPED_TEXTURES = 0x0000_0010; /// Supports textures that are cube arrays. const CUBE_ARRAY_TEXTURES = 0x0000_0020; + /// Supports comparison samplers. + const COMPARISON_SAMPLERS = 0x0000_0040; /// Supports samplers with anisotropic filtering const ANISOTROPIC_FILTERING = 0x0001_0000; /// All flags are in their compliant state. - const COMPLIANT = 0x0000_003F; + const COMPLIANT = 0x0000_007F; } } @@ -798,26 +800,6 @@ bitflags::bitflags! { } } -bitflags::bitflags! { - /// Flags controlling the shader processing. - /// - /// Note: These flags are internal tweaks, they don't affect the API. - #[repr(transparent)] - #[derive(Default)] - #[cfg_attr(feature = "trace", derive(serde::Serialize))] - #[cfg_attr(feature = "replay", derive(serde::Deserialize))] - pub struct ShaderFlags: u32 { - /// If enabled, `wgpu` will parse the shader with `Naga` - /// and validate it both internally and with regards to - /// the given pipeline interface. - const VALIDATION = 1; - /// If enabled, `wgpu` will attempt to operate on `Naga`'s internal - /// representation of the shader module for both validation and translation - /// into the backend shader language, on backends where `gfx-hal` supports this. - const EXPERIMENTAL_TRANSLATION = 2; - } -} - /// Dimensions of a particular texture view. #[repr(C)] #[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)] @@ -1063,6 +1045,16 @@ impl Default for PrimitiveTopology { } } +impl PrimitiveTopology { + /// Returns true for strip topologies. + pub fn is_strip(&self) -> bool { + match *self { + Self::PointList | Self::LineList | Self::TriangleList => false, + Self::LineStrip | Self::TriangleStrip => true, + } + } +} + /// Winding order which classifies the "front" face. #[repr(C)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] @@ -1446,12 +1438,12 @@ pub enum TextureFormat { /// [0, 255] converted to/from float [0, 1] in shader. /// /// [`Features::TEXTURE_COMPRESSION_ETC2`] must be enabled to use this texture format. - Etc2RgbA8Unorm = 56, + //Etc2RgbA8Unorm = 56, /// 4x4 block compressed texture. 16 bytes per block (8 bit/px). Complex pallet. 8 bit integer RGB + 8 bit alpha. /// Srgb-color [0, 255] converted to/from linear-color float [0, 1] in shader. /// /// [`Features::TEXTURE_COMPRESSION_ETC2`] must be enabled to use this texture format. - Etc2RgbA8UnormSrgb = 57, + //Etc2RgbA8UnormSrgb = 57, /// 4x4 block compressed texture. 8 bytes per block (4 bit/px). Complex pallet. 8 bit integer R. /// [0, 255] converted to/from float [0, 1] in shader. /// @@ -1716,8 +1708,8 @@ impl TextureFormat { Self::Etc2RgbUnormSrgb => (etc2, float, srgb, (4, 4), 8, basic), Self::Etc2RgbA1Unorm => (etc2, float, linear, (4, 4), 8, basic), Self::Etc2RgbA1UnormSrgb => (etc2, float, srgb, (4, 4), 8, basic), - Self::Etc2RgbA8Unorm => (etc2, float, linear, (4, 4), 16, basic), - Self::Etc2RgbA8UnormSrgb => (etc2, float, srgb, (4, 4), 16, basic), + //Self::Etc2RgbA8Unorm => (etc2, float, linear, (4, 4), 16, basic), + //Self::Etc2RgbA8UnormSrgb => (etc2, float, srgb, (4, 4), 16, basic), Self::EacRUnorm => (etc2, float, linear, (4, 4), 8, basic), Self::EacRSnorm => (etc2, float, linear, (4, 4), 8, basic), Self::EtcRgUnorm => (etc2, float, linear, (4, 4), 16, basic), @@ -2498,45 +2490,9 @@ impl Extent3d { /// assert_eq!(wgpu::Extent3d { width: 60, height: 60, depth_or_array_layers: 1 }.max_mips(), 6); /// assert_eq!(wgpu::Extent3d { width: 240, height: 1, depth_or_array_layers: 1 }.max_mips(), 8); /// ``` - pub fn max_mips(&self) -> u8 { + pub fn max_mips(&self) -> u32 { let max_dim = self.width.max(self.height.max(self.depth_or_array_layers)); - let max_levels = 32 - max_dim.leading_zeros(); - - max_levels as u8 - } - - /// Calculates the extent at a given mip level. - /// - /// If the given mip level is larger than possible, returns None. - /// - /// Treats the depth as part of the mipmaps. If calculating - /// for a 2DArray texture, which does not mipmap depth, set depth to 1. - /// - /// ```rust - /// # use wgpu_types as wgpu; - /// let extent = wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 }; - /// - /// assert_eq!(extent.at_mip_level(0), Some(wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(1), Some(wgpu::Extent3d { width: 50, height: 30, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(2), Some(wgpu::Extent3d { width: 25, height: 15, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(3), Some(wgpu::Extent3d { width: 12, height: 7, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(4), Some(wgpu::Extent3d { width: 6, height: 3, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(5), Some(wgpu::Extent3d { width: 3, height: 1, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(6), Some(wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 })); - /// assert_eq!(extent.at_mip_level(7), None); - /// ``` - pub fn at_mip_level(&self, level: u8) -> Option { - let mip_count = self.max_mips(); - - if level >= mip_count { - return None; - } - - Some(Self { - width: u32::max(1, self.width >> level as u32), - height: u32::max(1, self.height >> level as u32), - depth_or_array_layers: u32::max(1, self.depth_or_array_layers >> level as u32), - }) + 32 - max_dim.leading_zeros() } } @@ -2576,6 +2532,57 @@ impl TextureDescriptor { usage: self.usage, } } + + /// Calculates the extent at a given mip level. + /// + /// If the given mip level is larger than possible, returns None. + /// + /// Treats the depth as part of the mipmaps. If calculating + /// for a 2DArray texture, which does not mipmap depth, set depth to 1. + /// + /// ```rust + /// # use wgpu_types as wgpu; + /// let desc = wgpu::TextureDescriptor { + /// label: (), + /// size: Extent3d { width: 100, height: 60, depth_or_array_layers: 2 }, + /// mip_level_count: 7, + /// sample_count: 1, + /// dimension: wgpu::TextureDimension::D3, + /// format: wgpu::TextureFormat::Rgba8Sint, + /// usage: wgpu::TextureUsage::empty(), + /// }; + /// + /// assert_eq!(desc.mip_level_size(0), Some(wgpu::Extent3d { width: 100, height: 60, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(1), Some(wgpu::Extent3d { width: 50, height: 30, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(2), Some(wgpu::Extent3d { width: 25, height: 15, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(3), Some(wgpu::Extent3d { width: 12, height: 7, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(4), Some(wgpu::Extent3d { width: 6, height: 3, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(5), Some(wgpu::Extent3d { width: 3, height: 1, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(6), Some(wgpu::Extent3d { width: 1, height: 1, depth_or_array_layers: 1 })); + /// assert_eq!(desc.mip_level_size(7), None); + /// ``` + pub fn mip_level_size(&self, level: u32) -> Option { + if level >= self.mip_level_count { + return None; + } + + Some(Extent3d { + width: u32::max(1, self.size.width >> level), + height: u32::max(1, self.size.height >> level), + depth_or_array_layers: match self.dimension { + TextureDimension::D1 | TextureDimension::D2 => self.size.depth_or_array_layers, + TextureDimension::D3 => u32::max(1, self.size.depth_or_array_layers >> level), + }, + }) + } + + /// Returns the number of array layers. + pub fn array_layer_count(&self) -> u32 { + match self.dimension { + TextureDimension::D1 | TextureDimension::D2 => self.size.depth_or_array_layers, + TextureDimension::D3 => 1, + } + } } /// Kind of data the texture holds. @@ -3000,6 +3007,9 @@ pub struct ImageCopyTexture { /// The base texel of the texture in the selected `mip_level`. #[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))] pub origin: Origin3d, + /// The copy aspect. + #[cfg_attr(any(feature = "trace", feature = "replay"), serde(default))] + pub aspect: TextureAspect, } /// Subresource range within an image @@ -3042,7 +3052,9 @@ pub enum SamplerBorderColor { #[derive(Clone, Debug)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] #[cfg_attr(feature = "replay", derive(serde::Deserialize))] -pub struct QuerySetDescriptor { +pub struct QuerySetDescriptor { + /// Debug label for the query set. + pub label: L, /// Kind of query that this query set should contain. pub ty: QueryType, /// Total count of queries the set contains. Must not be zero. @@ -3050,11 +3062,24 @@ pub struct QuerySetDescriptor { pub count: u32, } +impl QuerySetDescriptor { + /// + pub fn map_label<'a, K>(&'a self, fun: impl FnOnce(&'a L) -> K) -> QuerySetDescriptor { + QuerySetDescriptor { + label: fun(&self.label), + ty: self.ty, + count: self.count, + } + } +} + /// Type of query contained in a QuerySet. #[derive(Copy, Clone, Debug)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] #[cfg_attr(feature = "replay", derive(serde::Deserialize))] pub enum QueryType { + /// Query returns a single 64-bit number, serving as an occlusion boolean. + Occlusion, /// Query returns up to 5 64-bit numbers based on the given flags. /// /// See [`PipelineStatisticsTypes`]'s documentation for more information diff --git a/wgpu/Cargo.toml b/wgpu/Cargo.toml index 3fe1259280..1c46125eba 100644 --- a/wgpu/Cargo.toml +++ b/wgpu/Cargo.toml @@ -20,8 +20,6 @@ default = [] trace = ["serde", "wgc/trace"] replay = ["serde", "wgc/replay"] webgl = ["wgc"] -# Enable SPIRV-Cross -cross = ["wgc/cross"] [target.'cfg(not(target_arch = "wasm32"))'.dependencies.wgc] package = "wgpu-core" @@ -38,6 +36,10 @@ optional = true package = "wgpu-types" path = "../wgpu-types" +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.hal] +package = "wgpu-hal" +path = "../wgpu-hal" + [dependencies] arrayvec = "0.5" log = "0.4" diff --git a/wgpu/examples/boids/main.rs b/wgpu/examples/boids/main.rs index cfd57c23ad..9bafcfe757 100644 --- a/wgpu/examples/boids/main.rs +++ b/wgpu/examples/boids/main.rs @@ -31,27 +31,17 @@ impl framework::Example for Example { /// constructs initial instance of Example struct fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { - // load and compile the shader - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgt::Backend::Vulkan | wgt::Backend::Metal | wgt::Backend::Gl => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION; - } - _ => {} //TODO - } let compute_shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("compute.wgsl"))), - flags, }); let draw_shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("draw.wgsl"))), - flags, }); // buffer for simulation parameters uniform diff --git a/wgpu/examples/bunnymark/main.rs b/wgpu/examples/bunnymark/main.rs index e89a17bbdc..cec3e5fe63 100644 --- a/wgpu/examples/bunnymark/main.rs +++ b/wgpu/examples/bunnymark/main.rs @@ -46,8 +46,9 @@ impl framework::Example for Example { ) -> Self { let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, - source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags: wgpu::ShaderFlags::all(), + source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!( + "../../../wgpu-hal/examples/halmark/shader.wgsl" + ))), }); let global_bind_group_layout = @@ -152,11 +153,7 @@ impl framework::Example for Example { usage: wgpu::TextureUsage::COPY_DST | wgpu::TextureUsage::SAMPLED, }); queue.write_texture( - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, + texture.as_image_copy(), &buf, wgpu::ImageDataLayout { offset: 0, diff --git a/wgpu/examples/capture/main.rs b/wgpu/examples/capture/main.rs index 319dea25ae..9682a9ac1f 100644 --- a/wgpu/examples/capture/main.rs +++ b/wgpu/examples/capture/main.rs @@ -94,11 +94,7 @@ async fn create_red_image_with_dimensions( // Copy the data from the texture to the buffer encoder.copy_texture_to_buffer( - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, + texture.as_image_copy(), wgpu::ImageCopyBuffer { buffer: &output_buffer, layout: wgpu::ImageDataLayout { diff --git a/wgpu/examples/conservative-raster/main.rs b/wgpu/examples/conservative-raster/main.rs index 763b7e9a7c..d0a6684541 100644 --- a/wgpu/examples/conservative-raster/main.rs +++ b/wgpu/examples/conservative-raster/main.rs @@ -90,7 +90,6 @@ impl framework::Example for Example { source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!( "triangle_and_lines.wgsl" ))), - flags: wgpu::ShaderFlags::all(), }); let pipeline_triangle_conservative = @@ -200,7 +199,6 @@ impl framework::Example for Example { let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("upscale.wgsl"))), - flags: wgpu::ShaderFlags::all(), }); ( device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { diff --git a/wgpu/examples/cube/main.rs b/wgpu/examples/cube/main.rs index a745791d0a..c2a5d24304 100644 --- a/wgpu/examples/cube/main.rs +++ b/wgpu/examples/cube/main.rs @@ -113,7 +113,7 @@ impl framework::Example for Example { fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, queue: &wgpu::Queue, ) -> Self { @@ -184,11 +184,7 @@ impl framework::Example for Example { }); let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default()); queue.write_texture( - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, + texture.as_image_copy(), &texels, wgpu::ImageDataLayout { offset: 0, @@ -223,17 +219,9 @@ impl framework::Example for Example { label: None, }); - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgpu::Backend::Metal | wgpu::Backend::Vulkan | wgpu::Backend::Gl => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION - } - _ => (), //TODO - } let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags, }); let vertex_buffers = [wgpu::VertexBufferLayout { diff --git a/wgpu/examples/hello-compute/main.rs b/wgpu/examples/hello-compute/main.rs index b9cdda8c53..2831014f2d 100644 --- a/wgpu/examples/hello-compute/main.rs +++ b/wgpu/examples/hello-compute/main.rs @@ -54,17 +54,10 @@ async fn execute_gpu(numbers: &[u32]) -> Option> { .await .unwrap(); - // Loads the shader from the SPIR-V file.arrayvec - let info = adapter.get_info(); - // skip this on LavaPipe temporarily - if info.vendor == 0x10005 { - return None; - } - + // Loads the shader from WGSL let cs_module = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags: wgpu::ShaderFlags::all(), }); // Gets the size in bytes of the buffer. diff --git a/wgpu/examples/hello-triangle/main.rs b/wgpu/examples/hello-triangle/main.rs index 23d8b3ff6e..4d27af83f0 100644 --- a/wgpu/examples/hello-triangle/main.rs +++ b/wgpu/examples/hello-triangle/main.rs @@ -35,7 +35,6 @@ async fn run(event_loop: EventLoop<()>, window: Window) { let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags: wgpu::ShaderFlags::all(), }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { diff --git a/wgpu/examples/mipmap/main.rs b/wgpu/examples/mipmap/main.rs index 00f1aaccea..0bda5fafb9 100644 --- a/wgpu/examples/mipmap/main.rs +++ b/wgpu/examples/mipmap/main.rs @@ -77,12 +77,10 @@ impl Example { texture: &wgpu::Texture, query_sets: &Option, mip_count: u32, - shader_flags: wgpu::ShaderFlags, ) { let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("blit.wgsl"))), - flags: shader_flags, }); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -206,7 +204,7 @@ impl framework::Example for Example { fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, queue: &wgpu::Queue, ) -> Self { @@ -249,11 +247,7 @@ impl framework::Example for Example { rows_per_image: None, }, }, - wgpu::ImageCopyTexture { - texture: &texture, - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - }, + texture.as_image_copy(), texture_extent, ); @@ -277,17 +271,9 @@ impl framework::Example for Example { }); // Create the render pipeline - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgpu::Backend::Metal | wgpu::Backend::Vulkan => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION - } - _ => (), //TODO - } let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("draw.wgsl"))), - flags, }); let draw_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { @@ -345,6 +331,7 @@ impl framework::Example for Example { // Create the timestamp query set. We need twice as many queries as we have passes, // as we need a query at the beginning and at the end of the operation. let timestamp = device.create_query_set(&wgpu::QuerySetDescriptor { + label: None, count: mip_passes * 2, ty: wgpu::QueryType::Timestamp, }); @@ -354,6 +341,7 @@ impl framework::Example for Example { // We only need one pipeline statistics query per pass. let pipeline_statistics = device.create_query_set(&wgpu::QuerySetDescriptor { + label: None, count: mip_passes, ty: wgpu::QueryType::PipelineStatistics( wgpu::PipelineStatisticsTypes::FRAGMENT_SHADER_INVOCATIONS, @@ -387,7 +375,6 @@ impl framework::Example for Example { &texture, &query_sets, MIP_LEVEL_COUNT, - flags, ); queue.submit(Some(init_encoder.finish())); diff --git a/wgpu/examples/msaa-line/main.rs b/wgpu/examples/msaa-line/main.rs index 0f02f089eb..9f90365e37 100644 --- a/wgpu/examples/msaa-line/main.rs +++ b/wgpu/examples/msaa-line/main.rs @@ -117,24 +117,16 @@ impl Example { impl framework::Example for Example { fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { log::info!("Press left/right arrow keys to change sample_count."); let sample_count = 4; - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgpu::Backend::Metal | wgpu::Backend::Vulkan => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION - } - _ => (), //TODO - } let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags, }); let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { diff --git a/wgpu/examples/shadow/main.rs b/wgpu/examples/shadow/main.rs index 7b48b846b9..2ea56d5443 100644 --- a/wgpu/examples/shadow/main.rs +++ b/wgpu/examples/shadow/main.rs @@ -215,7 +215,7 @@ impl framework::Example for Example { fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, _queue: &wgpu::Queue, ) -> Self { @@ -440,17 +440,9 @@ impl framework::Example for Example { attributes: &vertex_attr, }; - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgpu::Backend::Metal | wgpu::Backend::Vulkan => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION; - } - _ => (), //TODO - } let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags, }); let shadow_pass = { diff --git a/wgpu/examples/skybox/main.rs b/wgpu/examples/skybox/main.rs index 4ebec3ee14..60a117e66c 100644 --- a/wgpu/examples/skybox/main.rs +++ b/wgpu/examples/skybox/main.rs @@ -103,7 +103,7 @@ impl framework::Example for Skybox { fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, queue: &wgpu::Queue, ) -> Self { @@ -176,17 +176,9 @@ impl framework::Example for Skybox { }); // Create the render pipeline - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgpu::Backend::Metal | wgpu::Backend::Vulkan => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION - } - _ => (), //TODO - } let shader = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: None, source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("shader.wgsl"))), - flags, }); let camera = Camera { diff --git a/wgpu/examples/texture-arrays/main.rs b/wgpu/examples/texture-arrays/main.rs index 1bc974d3aa..b4eb99c1c6 100644 --- a/wgpu/examples/texture-arrays/main.rs +++ b/wgpu/examples/texture-arrays/main.rs @@ -151,11 +151,7 @@ impl framework::Example for Example { let green_texture_view = green_texture.create_view(&wgpu::TextureViewDescriptor::default()); queue.write_texture( - wgpu::ImageCopyTexture { - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - texture: &red_texture, - }, + red_texture.as_image_copy(), &red_texture_data, wgpu::ImageDataLayout { offset: 0, @@ -165,11 +161,7 @@ impl framework::Example for Example { wgpu::Extent3d::default(), ); queue.write_texture( - wgpu::ImageCopyTexture { - mip_level: 0, - origin: wgpu::Origin3d::ZERO, - texture: &green_texture, - }, + green_texture.as_image_copy(), &green_texture_data, wgpu::ImageDataLayout { offset: 0, diff --git a/wgpu/examples/water/main.rs b/wgpu/examples/water/main.rs index 789d689772..f3711a1193 100644 --- a/wgpu/examples/water/main.rs +++ b/wgpu/examples/water/main.rs @@ -263,7 +263,7 @@ impl Example { impl framework::Example for Example { fn init( sc_desc: &wgpu::SwapChainDescriptor, - adapter: &wgpu::Adapter, + _adapter: &wgpu::Adapter, device: &wgpu::Device, queue: &wgpu::Queue, ) -> Self { @@ -486,22 +486,13 @@ impl framework::Example for Example { }); // Upload/compile them to GPU code. - let mut flags = wgpu::ShaderFlags::VALIDATION; - match adapter.get_info().backend { - wgpu::Backend::Metal | wgpu::Backend::Vulkan => { - flags |= wgpu::ShaderFlags::EXPERIMENTAL_TRANSLATION - } - _ => (), //TODO - } let terrain_module = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: Some("terrain"), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("terrain.wgsl"))), - flags, }); let water_module = device.create_shader_module(&wgpu::ShaderModuleDescriptor { label: Some("water"), source: wgpu::ShaderSource::Wgsl(Cow::Borrowed(include_str!("water.wgsl"))), - flags, }); // Create the render pipelines. These describe how the data will flow through the GPU, and what diff --git a/wgpu/src/backend/direct.rs b/wgpu/src/backend/direct.rs index 96bef19099..8805aca605 100644 --- a/wgpu/src/backend/direct.rs +++ b/wgpu/src/backend/direct.rs @@ -2,7 +2,7 @@ use crate::{ backend::{error::ContextError, native_gpu_future}, AdapterInfo, BindGroupDescriptor, BindGroupLayoutDescriptor, BindingResource, BufferBinding, CommandEncoderDescriptor, ComputePassDescriptor, ComputePipelineDescriptor, - DownlevelProperties, Features, Label, Limits, LoadOp, MapMode, Operations, + DownlevelCapabilities, Features, Label, Limits, LoadOp, MapMode, Operations, PipelineLayoutDescriptor, RenderBundleEncoderDescriptor, RenderPipelineDescriptor, SamplerDescriptor, ShaderModuleDescriptor, ShaderSource, SwapChainStatus, TextureDescriptor, TextureFormat, TextureViewDescriptor, @@ -50,6 +50,7 @@ impl Context { })) } + /*TODO: raw surface #[cfg(any(target_os = "ios", target_os = "macos"))] pub unsafe fn create_surface_from_core_animation_layer( self: &Arc, @@ -60,7 +61,7 @@ impl Context { context: Arc::clone(self), id, } - } + }*/ fn handle_error( &self, @@ -540,6 +541,7 @@ fn map_texture_copy_view(view: crate::ImageCopyTexture) -> wgc::command::ImageCo texture: view.texture.id.id, mip_level: view.mip_level, origin: view.origin, + aspect: view.aspect, } } @@ -731,7 +733,7 @@ impl crate::Context for Context { } } - fn adapter_downlevel_properties(&self, adapter: &Self::AdapterId) -> DownlevelProperties { + fn adapter_downlevel_properties(&self, adapter: &Self::AdapterId) -> DownlevelCapabilities { let global = &self.0; match wgc::gfx_select!(*adapter => global.adapter_downlevel_properties(*adapter)) { Ok(downlevel) => downlevel, @@ -776,7 +778,7 @@ impl crate::Context for Context { } } - fn device_downlevel_properties(&self, device: &Self::DeviceId) -> DownlevelProperties { + fn device_downlevel_properties(&self, device: &Self::DeviceId) -> DownlevelCapabilities { let global = &self.0; match wgc::gfx_select!(device.id => global.device_downlevel_properties(device.id)) { Ok(limits) => limits, @@ -806,7 +808,6 @@ impl crate::Context for Context { let global = &self.0; let descriptor = wgc::pipeline::ShaderModuleDescriptor { label: desc.label.map(Borrowed), - flags: desc.flags, }; let source = match desc.source { ShaderSource::SpirV(ref spv) => wgc::pipeline::ShaderModuleSource::SpirV(Borrowed(spv)), @@ -951,20 +952,20 @@ impl crate::Context for Context { device: &Self::DeviceId, desc: &PipelineLayoutDescriptor, ) -> Self::PipelineLayoutId { - // Limit is always less or equal to wgc::MAX_BIND_GROUPS, so this is always right + // Limit is always less or equal to hal::MAX_BIND_GROUPS, so this is always right // Guards following ArrayVec assert!( - desc.bind_group_layouts.len() <= wgc::MAX_BIND_GROUPS, + desc.bind_group_layouts.len() <= hal::MAX_BIND_GROUPS, "Bind group layout count {} exceeds device bind group limit {}", desc.bind_group_layouts.len(), - wgc::MAX_BIND_GROUPS + hal::MAX_BIND_GROUPS ); let temp_layouts = desc .bind_group_layouts .iter() .map(|bgl| bgl.id) - .collect::>(); + .collect::>(); let descriptor = wgc::binding_model::PipelineLayoutDescriptor { label: desc.label.map(Borrowed), bind_group_layouts: Borrowed(&temp_layouts), @@ -996,7 +997,7 @@ impl crate::Context for Context { ) -> Self::RenderPipelineId { use wgc::pipeline as pipe; - let vertex_buffers: ArrayVec<[_; wgc::device::MAX_VERTEX_BUFFERS]> = desc + let vertex_buffers: ArrayVec<[_; hal::MAX_VERTEX_BUFFERS]> = desc .vertex .buffers .iter() @@ -1011,7 +1012,7 @@ impl crate::Context for Context { Some(_) => None, None => Some(wgc::device::ImplicitPipelineIds { root_id: PhantomData, - group_ids: &[PhantomData; wgc::MAX_BIND_GROUPS], + group_ids: &[PhantomData; hal::MAX_BIND_GROUPS], }), }; let descriptor = pipe::RenderPipelineDescriptor { @@ -1071,7 +1072,7 @@ impl crate::Context for Context { Some(_) => None, None => Some(wgc::device::ImplicitPipelineIds { root_id: PhantomData, - group_ids: &[PhantomData; wgc::MAX_BIND_GROUPS], + group_ids: &[PhantomData; hal::MAX_BIND_GROUPS], }), }; let descriptor = pipe::ComputePipelineDescriptor { @@ -1206,12 +1207,12 @@ impl crate::Context for Context { fn device_create_query_set( &self, device: &Self::DeviceId, - desc: &wgt::QuerySetDescriptor, + desc: &wgt::QuerySetDescriptor