diff --git a/.cargo/config.toml b/.cargo/config.toml index b97153d60ad..16bb815038f 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -1,2 +1,6 @@ [alias] xtask = "run -p wgpu-xtask --" + +# Needed until https://github.com/rust-random/getrandom/issues/675 is resolved. +[target.wasm32-unknown-unknown] +rustflags = ["--cfg", "getrandom_backend=\"wasm_js\""] diff --git a/.config/nextest.toml b/.config/nextest.toml index 64b3b84cba3..574ad1b3789 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -6,6 +6,10 @@ slow-timeout = { period = "45s", terminate-after = 2 } fail-fast = false retries = 0 +[profile.default-miri] +# Miri is very very slow, so give it a much longer timeout. +slow-timeout = { period = "120s", terminate-after = 4 } + # Use two threads for tests with "2 threads" in their name [[profile.default.overrides]] filter = 'test(~2_threads) | test(~2 threads)' @@ -38,3 +42,12 @@ slow-timeout = { period = "3m", terminate-after = 2 } [[profile.default.overrides]] filter = 'test(~oom_test)' threads-required = "num-test-threads" + +# +# Priorities for slow tests so that they run first, increasing overall test suite speed. +# On software renderers, they can take 10-60 seconds. Compile fail can easily take 30+ +# seconds as it has to compile wgpu. +# +[[profile.default.overrides]] +priority = 1 +filter = 'test(clear_texture) | test(clear_buffer_range_respected) | test(compile_fail) | test(test_api)' diff --git a/.deny.toml b/.deny.toml index 4e690a0cecb..4634adcac85 100644 --- a/.deny.toml +++ b/.deny.toml @@ -2,17 +2,12 @@ multiple-versions = "deny" skip-tree = [ { name = "windows-sys", version = "0.45" }, + { name = "windows-sys", version = "0.59" }, { name = "winit", version = "0.29" }, - { name = "rustc_version", version = "0.2.3" }, - { name = "miniz_oxide", version = "0.7.4" }, + { name = "rustc-hash", version = "1.1.0" }, # introduced by Deno, to be investigated - { name = "strum_macros", version = "0.25.3" }, { name = "petgraph", version = "0.6.5" }, - { name = "base64-simd", version = "0.7.0" }, - { name = "bit-set", version = "0.5.3" }, - { name = "bit-vec", version = "0.6.3" }, - { name = "capacity_builder", version = "0.1.3" }, ] skip = [ # Flume uses an old version @@ -21,6 +16,9 @@ skip = [ # Deno uses an old version { name = "which", version = "6.0.3" }, + # Winit uses an old version + { name = "windows-sys", version = "0.52.0" }, + # Loom uses a new windows version { name = "windows", version = "0.61.1" }, { name = "windows-core", version = "0.61.2" }, @@ -29,27 +27,28 @@ skip = [ { name = "windows-result", version = "0.3.4" }, { name = "windows-strings", version = "0.4.2" }, - # cargo-metadata uses an old version of ordered-float. Only used for testing. - { name = "ordered-float", version = "2.10.1" }, - # criterion uses an old version - { name = "itertools", version = "0.10.5" }, # bindgen (used by deno) uses old version { name = "itertools", version = "0.13.0" }, - # loom (used by tracy-client) uses old `matchers` crate - { name = "regex-automata", version = "0.1.10" }, - { name = "regex-syntax", version = "0.6.29" }, - # Strum uses an old version - { name = "heck", version = "0.4.0" }, # Deno uses an old version - { name = "strum", version = "0.25.0" }, + { name = "bincode", version = "1.3.3" }, ] wildcards = "deny" allow-wildcard-paths = true [advisories] -# `paste` crate is no longer maintained https://rustsec.org/advisories/RUSTSEC-2024-0436 -# It's a dependency of `metal` (which is to be replaced with `objc2`) and a transitive dependency of `deno`. -ignore = ["RUSTSEC-2024-0436"] +ignore = [ + # `paste` crate is no longer maintained https://rustsec.org/advisories/RUSTSEC-2024-0436 + # It's a dependency of `metal` (which is to be replaced with `objc2-metal`), and a + # transitive dependency of `deno`. https://github.com/gfx-rs/wgpu/issues/7873 + "RUSTSEC-2024-0436", + # `unic-*` crates are no longer maintained https://rustsec.org/advisories/RUSTSEC-2025-0100 + # These are used via `deno`. https://github.com/gfx-rs/wgpu/issues/8393 + "RUSTSEC-2025-0075", + "RUSTSEC-2025-0080", + "RUSTSEC-2025-0081", + "RUSTSEC-2025-0098", + "RUSTSEC-2025-0100", +] [licenses] allow = [ @@ -62,7 +61,6 @@ allow = [ "MPL-2.0", "MIT", "MIT-0", - "Unicode-DFS-2016", "Unicode-3.0", "Zlib", ] @@ -70,11 +68,11 @@ private = { ignore = true } [sources] allow-git = [ - # Waiting on releases; used in examples only + # Waiting on releases; used in examples/tests only ] unknown-registry = "deny" unknown-git = "deny" required-git-spec = "rev" [sources.allow-org] -github = ["gfx-rs"] +github = [] diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index 1cae09787e1..00000000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,17 +0,0 @@ -* @gfx-rs/wgpu - -/cts_runner/ @crowlkats @gfx-rs/wgpu -/deno_webgpu/ @crowlkats @gfx-rs/wgpu -/naga/ @gfx-rs/naga -/naga-cli/ @gfx-rs/naga - -# Both wgpu and naga teams are owners of naga infrastructure so -# either team can review changes to deps and docs. -naga/Cargo.toml @gfx-rs/wgpu @gfx-rs/naga -naga/README.md @gfx-rs/wgpu @gfx-rs/naga -naga/CHANGELOG.md @gfx-rs/wgpu @gfx-rs/naga -naga-cli/Cargo.toml @gfx-rs/wgpu @gfx-rs/naga - -# We leave the codeowners empty for the changelog, so naga changes -# don't trigger wgpu reviews and vise versa. -/CHANGELOG.md diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index dc45ceff329..29ab84ed12c 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,5 +1,5 @@ blank_issues_enabled: false contact_links: - name: Question about wgpu - url: https://github.com/gfx-rs/wgpu/discussions/new + url: https://github.com/gfx-rs/wgpu/discussions/new/choose about: Any questions about how to use wgpu should go here. diff --git a/.github/actions/install-mesa/action.yml b/.github/actions/install-mesa/action.yml index d6cbb580895..103c1e218f9 100644 --- a/.github/actions/install-mesa/action.yml +++ b/.github/actions/install-mesa/action.yml @@ -1,16 +1,22 @@ name: 'Install Mesa' description: 'Install Mesa' +inputs: + # Sourced from https://archive.mesa3d.org/. Bumping this requires + # updating the mesa build in https://github.com/gfx-rs/ci-build and creating a new release. + version: + default: '24.3.4' + # Corresponds to https://github.com/gfx-rs/ci-build/releases + ci-binary-build: + default: 'build20' runs: - using: "composite" + using: 'composite' steps: - - name: Install Mesa + - name: (Linux) Install Mesa + if: runner.os == 'Linux' shell: bash env: - # Sourced from https://archive.mesa3d.org/. Bumping this requires - # updating the mesa build in https://github.com/gfx-rs/ci-build and creating a new release. - MESA_VERSION: "24.3.4" - # Corresponds to https://github.com/gfx-rs/ci-build/releases - CI_BINARY_BUILD: "build20" + MESA_VERSION: ${{ inputs.version }} + CI_BINARY_BUILD: ${{ inputs.ci-binary-build }} run: | set -e @@ -34,3 +40,22 @@ runs: echo "VK_DRIVER_FILES=$PWD/icd.json" >> "$GITHUB_ENV" echo "LD_LIBRARY_PATH=$PWD/mesa/lib/x86_64-linux-gnu/:$LD_LIBRARY_PATH" >> "$GITHUB_ENV" echo "LIBGL_DRIVERS_PATH=$PWD/mesa/lib/x86_64-linux-gnu/dri" >> "$GITHUB_ENV" + + - name: (Windows) Install Mesa + if: runner.os == 'Windows' + shell: bash + env: + MESA_VERSION: ${{ inputs.version }} + CI_BINARY_BUILD: ${{ inputs.ci-binary-build }} + run: | + set -e + + curl.exe -L --retry 5 https://github.com/pal1000/mesa-dist-win/releases/download/$MESA_VERSION/mesa3d-$MESA_VERSION-release-msvc.7z -o mesa.7z + 7z.exe e mesa.7z -omesa x64/{opengl32.dll,libgallium_wgl.dll,libglapi.dll,vulkan_lvp.dll,lvp_icd.x86_64.json} + + cp -v mesa/* target/llvm-cov-target/debug/ + cp -v mesa/* target/llvm-cov-target/debug/deps + + # We need to use cygpath to convert PWD to a windows path as we're using bash. + echo "VK_DRIVER_FILES=`cygpath --windows $PWD/mesa/lvp_icd.x86_64.json`" >> "$GITHUB_ENV" + echo "GALLIUM_DRIVER=llvmpipe" >> "$GITHUB_ENV" diff --git a/.github/actions/install-vulkan-sdk/action.yml b/.github/actions/install-vulkan-sdk/action.yml new file mode 100644 index 00000000000..e53dfa2a25f --- /dev/null +++ b/.github/actions/install-vulkan-sdk/action.yml @@ -0,0 +1,62 @@ +name: 'Install Vulkan SDK' +description: 'Install Vulkan SDK' +inputs: + # Sourced from https://vulkan.lunarg.com/sdk/home#linux + version: + default: '1.4.321' + full-version: + default: '1.4.321.0' +runs: + using: 'composite' + steps: + - name: (Linux) Install Vulkan SDK + if: runner.os == 'Linux' + shell: bash + env: + VULKAN_SDK_VERSION: ${{ inputs.version }} + VULKAN_FULL_SDK_VERSION: ${{ inputs.full-version }} + run: | + set -e + + curl -L --retry 5 https://sdk.lunarg.com/sdk/download/${{ env.VULKAN_FULL_SDK_VERSION }}/linux/vulkansdk-linux-x86_64-${{ env.VULKAN_FULL_SDK_VERSION }}.tar.xz -o vulkan-sdk.tar.xz + mkdir vulkan-sdk + tar xpf vulkan-sdk.tar.xz -C vulkan-sdk + + mv ./vulkan-sdk/${{ env.VULKAN_FULL_SDK_VERSION }} $HOME/VulkanSDK + + echo "$HOME/VulkanSDK/x86_64/bin" >> "$GITHUB_PATH" + echo "LD_LIBRARY_PATH=$HOME/VulkanSDK/x86_64/lib${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}" >> "$GITHUB_ENV" + echo "VK_ADD_LAYER_PATH=$HOME/VulkanSDK/x86_64/share/vulkan/explicit_layer.d" >> "$GITHUB_ENV" + + - name: (Windows) Install Vulkan SDK + if: runner.os == 'Windows' + shell: bash + env: + VULKAN_SDK_VERSION: ${{ inputs.version }} + VULKAN_FULL_SDK_VERSION: ${{ inputs.full-version }} + run: | + set -e + + curl.exe -L --retry 5 https://sdk.lunarg.com/sdk/download/${{ env.VULKAN_FULL_SDK_VERSION }}/windows/vulkansdk-windows-X64-${{ env.VULKAN_FULL_SDK_VERSION }}.exe -o vulkan-sdk-installer.exe + + ./vulkan-sdk-installer.exe --accept-licenses --default-answer --confirm-command install + + echo "C:/VulkanSDK/${{ env.VULKAN_FULL_SDK_VERSION }}/Bin" >> "$GITHUB_PATH" + + + - name: (Mac) Install Vulkan SDK + if: runner.os == 'macOS' + shell: bash + env: + VULKAN_SDK_VERSION: ${{ inputs.version }} + VULKAN_FULL_SDK_VERSION: ${{ inputs.full-version }} + run: | + set -e + + curl -L --retry 5 https://sdk.lunarg.com/sdk/download/${{ env.VULKAN_FULL_SDK_VERSION }}/mac/vulkansdk-macos-${{ env.VULKAN_FULL_SDK_VERSION }}.zip -o vulkan-sdk.zip + unzip vulkan-sdk.zip -d vulkan-sdk + + ls -l vulkan-sdk + sudo ./vulkan-sdk/vulkansdk-macOS-${{ env.VULKAN_FULL_SDK_VERSION }}.app/Contents/MacOS/vulkansdk-macOS-${{ env.VULKAN_FULL_SDK_VERSION }} --root "$HOME/VulkanSDK" --accept-licenses --default-answer --confirm-command install + + echo "$HOME/VulkanSDK/macOS/bin" >> "$GITHUB_PATH" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index eaea55ec01e..54d8561f16f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,12 +3,13 @@ name: CI on: push: branches-ignore: [ - # We don't need to run on renovate PRs. + # Renovate branches are always PRs, so they will be covered + # by the pull_request event. "renovate/**", - # This is the branch the merge queue creates. + # Branches with the `gh-readonly-queue` prefix are used by the + # merge queue, so they are already covered by the `merge_group` event. "gh-readonly-queue/**", ] - tags: [v0.*] pull_request: merge_group: @@ -17,15 +18,8 @@ env: # Dependency versioning # - # Sourced from https://vulkan.lunarg.com/sdk/home#linux - VULKAN_SDK_VERSION: "1.4.313" - VULKAN_FULL_SDK_VERSION: "1.4.313.0" - - # These Mesa version definition is duplicated in the install-mesa action. - MESA_VERSION: "24.3.4" - # This is the MSRV used by `wgpu` itself and all surrounding infrastructure. - REPO_MSRV: "1.84" + REPO_MSRV: "1.88" # This is the MSRV used by the `wgpu-core`, `wgpu-hal`, and `wgpu-types` crates, # to ensure that they can be used with firefox. CORE_MSRV: "1.82.0" @@ -37,7 +31,7 @@ env: CARGO_INCREMENTAL: false CARGO_TERM_COLOR: always WGPU_DX12_COMPILER: dxc - RUST_LOG: debug + RUST_LOG: debug,wasm_bindgen_wasm_interpreter=warn,wasm_bindgen_cli_support=warn,walrus=warn RUST_BACKTRACE: full PKG_CONFIG_ALLOW_CROSS: 1 # allow android to work RUSTFLAGS: -D warnings @@ -46,6 +40,13 @@ env: CACHE_SUFFIX: d # cache busting WGPU_CI: true +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + # We distinguish the following kinds of builds: # - native: build for the same target as we compile on # - web: build for the Web @@ -115,60 +116,67 @@ jobs: # Linux - name: Linux x86_64 - os: ubuntu-22.04 + os: ubuntu-24.04 target: x86_64-unknown-linux-gnu tier: 1 kind: native - name: Linux aarch64 - os: ubuntu-22.04 + os: ubuntu-24.04 target: aarch64-unknown-linux-gnu tier: 1 kind: native + # FreeBSD + - name: FreeBSD x86_64 + os: ubuntu-24.04 + target: x86_64-unknown-freebsd + tier: 2 + kind: wgpu-only + # Android - name: Android aarch64 - os: ubuntu-22.04 + os: ubuntu-24.04 target: aarch64-linux-android tier: 2 kind: native # Android - name: Android ARMv7 - os: ubuntu-22.04 + os: ubuntu-24.04 target: armv7-linux-androideabi tier: 2 kind: wgpu-only # Android - name: Android x86_64 - os: ubuntu-22.04 + os: ubuntu-24.04 target: x86_64-linux-android tier: 2 kind: wgpu-only # OpenHarmony - name: OpenHarmony aarch64 - os: ubuntu-22.04 + os: ubuntu-24.04 target: aarch64-unknown-linux-ohos tier: 2 kind: native # WebGPU/WebGL - name: WebAssembly - os: ubuntu-22.04 + os: ubuntu-24.04 target: wasm32-unknown-unknown tier: 2 kind: web - name: Emscripten - os: ubuntu-22.04 + os: ubuntu-24.04 target: wasm32-unknown-emscripten tier: 2 kind: wgpu-only - name: WebAssembly Core 1.0 - os: ubuntu-22.04 + os: ubuntu-24.04 target: wasm32v1-none tier: 2 kind: no_std @@ -176,7 +184,7 @@ jobs: # 32-bit PowerPC Linux # Included to test support for `portable-atomic` - name: Linux ppc32 - os: ubuntu-22.04 + os: ubuntu-24.04 target: powerpc-unknown-linux-gnu tier: 2 kind: wgpu-only @@ -186,7 +194,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install toolchain (repo MSRV - tier 1 or 2) if: matrix.tier == 1 || matrix.tier == 2 @@ -253,6 +261,8 @@ jobs: run: | set -e + export RUSTFLAGS="$RUSTFLAGS --cfg getrandom_backend=\"wasm_js\"" + # build for WebGPU cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --features glsl,spirv,fragile-send-sync-non-atomic-wasm cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --features glsl,spirv @@ -313,6 +323,10 @@ jobs: # Check with all features. cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --benches --all-features + # Check with all features and profiling macro code. + # If we don't check this then errors inside `profiling::scope!()` will not be caught. + cargo clippy --target ${{ matrix.target }} ${{ matrix.extra-flags }} --tests --benches --all-features --features test-build-with-profiling + # build docs cargo doc --target ${{ matrix.target }} ${{ matrix.extra-flags }} --all-features --no-deps @@ -353,7 +367,7 @@ jobs: # Linux - name: Linux x86_64 - os: ubuntu-22.04 + os: ubuntu-24.04 target: x86_64-unknown-linux-gnu name: MSRV Check ${{ matrix.name }} @@ -361,7 +375,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install core MSRV toolchain run: | @@ -389,6 +403,8 @@ jobs: run: | set -e + # 1.23.2 requires bytemuck_derive 1.10.1 + cargo update -p bytemuck --precise 1.23.1 # 1.9.0 requires MSRV 1.84 cargo update -p bytemuck_derive --precise 1.8.1 @@ -408,7 +424,7 @@ jobs: timeout-minutes: 10 name: MSRV Minimal Versions - runs-on: ubuntu-22.04 + runs-on: ubuntu-24.04 env: # Override flags to NOT include `-D warnings`, because warnings may be due to harmless problems in deps. # Also, allow unexpected_cfgs because it is very common and spammy when using old deps. @@ -416,7 +432,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install toolchain run: | @@ -457,7 +473,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install repo MSRV toolchain run: | @@ -473,6 +489,9 @@ jobs: - name: Execute tests run: | cd wgpu + + export RUSTFLAGS="$RUSTFLAGS --cfg getrandom_backend=\"wasm_js\"" + wasm-pack test --headless --chrome --no-default-features --features wgsl,webgl,web --workspace gpu-test: @@ -500,7 +519,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install repo MSRV toolchain run: | @@ -543,60 +562,19 @@ jobs: - name: (Windows) Install Mesa if: matrix.os == 'windows-2022' - shell: bash - run: | - set -e - - curl.exe -L --retry 5 https://github.com/pal1000/mesa-dist-win/releases/download/$MESA_VERSION/mesa3d-$MESA_VERSION-release-msvc.7z -o mesa.7z - 7z.exe e mesa.7z -omesa x64/{opengl32.dll,libgallium_wgl.dll,libglapi.dll,vulkan_lvp.dll,lvp_icd.x86_64.json} - - cp -v mesa/* target/llvm-cov-target/debug/ - cp -v mesa/* target/llvm-cov-target/debug/deps - - # We need to use cygpath to convert PWD to a windows path as we're using bash. - echo "VK_DRIVER_FILES=`cygpath --windows $PWD/mesa/lvp_icd.x86_64.json`" >> "$GITHUB_ENV" - echo "GALLIUM_DRIVER=llvmpipe" >> "$GITHUB_ENV" + uses: ./.github/actions/install-mesa - name: (Windows) Install Vulkan SDK if: matrix.os == 'windows-2022' - shell: bash - run: | - set -e - - curl.exe -L --retry 5 https://sdk.lunarg.com/sdk/download/${{ env.VULKAN_FULL_SDK_VERSION }}/windows/vulkansdk-windows-X64-${{ env.VULKAN_FULL_SDK_VERSION }}.exe -o vulkan-sdk-installer.exe - - ./vulkan-sdk-installer.exe --accept-licenses --default-answer --confirm-command install - - echo "C:/VulkanSDK/${{ env.VULKAN_FULL_SDK_VERSION }}/Bin" >> "$GITHUB_PATH" + uses: ./.github/actions/install-vulkan-sdk - name: (Mac) Install Vulkan SDK if: matrix.os == 'macos-14' - shell: bash - run: | - set -e - - curl -L --retry 5 https://sdk.lunarg.com/sdk/download/${{ env.VULKAN_FULL_SDK_VERSION }}/mac/vulkansdk-macos-${{ env.VULKAN_FULL_SDK_VERSION }}.zip -o vulkan-sdk.zip - unzip vulkan-sdk.zip -d vulkan-sdk - - ls -l vulkan-sdk - sudo ./vulkan-sdk/vulkansdk-macOS-${{ env.VULKAN_FULL_SDK_VERSION }}.app/Contents/MacOS/vulkansdk-macOS-${{ env.VULKAN_FULL_SDK_VERSION }} --root "$HOME/VulkanSDK" --accept-licenses --default-answer --confirm-command install - - echo "$HOME/VulkanSDK/macOS/bin" >> "$GITHUB_PATH" + uses: ./.github/actions/install-vulkan-sdk - name: (Linux) Install Vulkan SDK if: matrix.os == 'ubuntu-24.04' - shell: bash - run: | - set -e - - sudo apt-get update -y -qq - - # vulkan sdk - wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-${{ env.VULKAN_SDK_VERSION }}-noble.list https://packages.lunarg.com/vulkan/${{ env.VULKAN_SDK_VERSION }}/lunarg-vulkan-$VULKAN_SDK_VERSION-noble.list - - sudo apt-get update - sudo apt install -y vulkan-sdk + uses: ./.github/actions/install-vulkan-sdk - name: (Linux) Install Mesa if: matrix.os == 'ubuntu-24.04' @@ -664,7 +642,7 @@ jobs: steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install repo MSRV toolchain run: | @@ -692,7 +670,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install repo MSRV toolchain run: | @@ -713,7 +691,7 @@ jobs: run: taplo format --check --diff - name: Check for typos - uses: crate-ci/typos@v1.34.0 + uses: crate-ci/typos@v1.36.3 check-cts-runner: # runtime is normally 2 minutes @@ -723,7 +701,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install MSRV toolchain run: | @@ -761,13 +739,14 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Run `cargo deny check` uses: EmbarkStudios/cargo-deny-action@v2 with: command: check advisories arguments: --all-features --workspace + command-arguments: -Dwarnings -Aunmatched-organization rust-version: ${{ env.REPO_MSRV }} cargo-deny-check-rest: @@ -778,11 +757,12 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Run `cargo deny check` uses: EmbarkStudios/cargo-deny-action@v2 with: command: check bans licenses sources arguments: --all-features --workspace + command-arguments: -Dwarnings -Aunmatched-organization rust-version: ${{ env.REPO_MSRV }} diff --git a/.github/workflows/cts.yml b/.github/workflows/cts.yml index 418a46c986b..317968b78e2 100644 --- a/.github/workflows/cts.yml +++ b/.github/workflows/cts.yml @@ -1,17 +1,30 @@ name: CTS on: + push: + branches-ignore: [ + # Renovate branches are always PRs, so they will be covered + # by the pull_request event. + "renovate/**", + # Branches with the `gh-readonly-queue` prefix are used by the + # merge queue, so they are already covered by the `merge_group` event. + "gh-readonly-queue/**", + ] pull_request: - types: [labeled, opened, synchronize] - schedule: - - cron: "33 2 * * *" - workflow_dispatch: + merge_group: env: CARGO_INCREMENTAL: false CARGO_TERM_COLOR: always RUST_BACKTRACE: full - MSRV: "1.84" + MSRV: "1.88" + +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} jobs: cts: @@ -23,34 +36,42 @@ jobs: - name: Windows x86_64 os: windows-2022 target: x86_64-pc-windows-msvc + backend: dx12 # Mac - name: Mac aarch64 os: macos-14 target: x86_64-apple-darwin + backend: metal # Linux - name: Linux x86_64 os: ubuntu-24.04 target: x86_64-unknown-linux-gnu + backend: vulkan name: CTS ${{ matrix.name }} runs-on: ${{ matrix.os }} steps: - name: checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Install Repo MSRV toolchain run: | - rustup toolchain install ${{ env.MSRV }} --no-self-update --profile=minimal --target ${{ matrix.target }} + rustup toolchain install ${{ env.MSRV }} --no-self-update --profile=minimal --target ${{ matrix.target }} --component llvm-tools rustup override set ${{ env.MSRV }} cargo -V + - name: Install `cargo-llvm-cov` + uses: taiki-e/install-action@v2 + with: + tool: cargo-llvm-cov + - name: caching uses: Swatinem/rust-cache@v2 with: - prefix-key: v2-rust # Increment version for cache busting + prefix-key: v2-rust-wgpu27 # Increment version for cache busting cache-directories: cts # We enable line numbers for panics, but that's it @@ -60,7 +81,7 @@ jobs: mkdir -p .cargo cat <> .cargo/config.toml [profile.dev] - debug = 1 + debug = "line-tables-only" EOF - name: (Windows) Install DXC @@ -71,7 +92,7 @@ jobs: if: matrix.os == 'windows-2022' uses: ./.github/actions/install-warp with: - target-dirs: "target/debug" + target-dirs: "target/llvm-cov-target/debug" - name: (Linux) Install Mesa if: matrix.os == 'ubuntu-24.04' @@ -79,4 +100,20 @@ jobs: - name: run CTS shell: bash - run: cargo xtask cts + run: cargo xtask cts --llvm-cov --backend ${{ matrix.backend }} + + - name: Generate coverage report + id: coverage + shell: bash + continue-on-error: true + run: | + set -e + + cargo llvm-cov report --lcov --output-path lcov.info + + - name: Upload coverage report to Codecov + uses: codecov/codecov-action@v5 + if: steps.coverage.outcome == 'success' + with: + files: lcov.info + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index d96ed087096..83adc7629ec 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -10,19 +10,26 @@ on: env: # This is the MSRV used by `wgpu` itself and all surrounding infrastructure. - REPO_MSRV: "1.84" + REPO_MSRV: "1.88" CARGO_INCREMENTAL: false CARGO_TERM_COLOR: always RUST_BACKTRACE: full +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: build: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: persist-credentials: false diff --git a/.github/workflows/generate.yml b/.github/workflows/generate.yml index 0f758d4a839..cd2d4af901e 100644 --- a/.github/workflows/generate.yml +++ b/.github/workflows/generate.yml @@ -2,8 +2,14 @@ name: cargo-generate on: push: - branches: ["*"] - tags: [v0.*] + branches-ignore: [ + # Renovate branches are always PRs, so they will be covered + # by the pull_request event. + "renovate/**", + # Branches with the `gh-readonly-queue` prefix are used by the + # merge queue, so they are already covered by the `merge_group` event. + "gh-readonly-queue/**", + ] pull_request: merge_group: @@ -13,9 +19,16 @@ env: # # This is the MSRV used by `wgpu` itself and all surrounding infrastructure. - REPO_MSRV: "1.84" + REPO_MSRV: "1.88" RUSTFLAGS: -D warnings +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: cargo-generate: timeout-minutes: 5 @@ -36,7 +49,7 @@ jobs: name: "${{ matrix.name }}" steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 # We can't rely on an override here, as that would only set # the toolchain for the current directory, not the newly generated project. diff --git a/.github/workflows/lazy.yml b/.github/workflows/lazy.yml index d33f4042368..d0e526799b1 100644 --- a/.github/workflows/lazy.yml +++ b/.github/workflows/lazy.yml @@ -3,7 +3,7 @@ name: Lazy on: pull_request: paths: - - '.github/workflows/lazy.yml' + - ".github/workflows/lazy.yml" push: branches: [trunk] @@ -12,12 +12,19 @@ env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: parse-dota2: name: "Validate Shaders: Dota2" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - run: mkdir naga/data @@ -43,7 +50,7 @@ jobs: name: "Validate Shaders: Sascha Willems Vulkan Tutorial" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Download shaders run: cd naga && git clone https://github.com/SaschaWillems/Vulkan.git @@ -78,7 +85,7 @@ jobs: name: "Validate Shaders: dneto0 spirv-samples" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Download shaders run: | @@ -98,58 +105,58 @@ jobs: - name: Compile `spv` from `spvasm` run: | - cd naga/spirv-samples - mkdir -p spv + cd naga/spirv-samples + mkdir -p spv - find "./spvasm" -name '*.spvasm' | while read fname; - do - echo "Convert to spv with spirv-as: $fname" - ../install/bin/spirv-as --target-env spv1.3 $(realpath ${fname}) -o ./spv/$(basename ${fname}).spv - done; + find "./spvasm" -name '*.spvasm' | while read fname; + do + echo "Convert to spv with spirv-as: $fname" + ../install/bin/spirv-as --target-env spv1.3 $(realpath ${fname}) -o ./spv/$(basename ${fname}).spv + done; - name: Validate `spv` and generate `wgsl` run: | - set +e - cd naga/spirv-samples - SUCCESS_RESULT_COUNT=0 - FILE_COUNT=0 - mkdir -p spv - mkdir -p wgsl - - echo "==== Validate spv and generate wgsl ====" - rm -f counter - touch counter - - find "./spv" -name '*.spv' | while read fname; - do - echo "Convert: $fname" - FILE_COUNT=$((FILE_COUNT+1)) - ../../target/release/naga --validate 27 $(realpath ${fname}) ./wgsl/$(basename ${fname}).wgsl - if [[ $? -eq 0 ]]; then - SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1)) - fi - echo "Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT" > counter - done - cat counter + set +e + cd naga/spirv-samples + SUCCESS_RESULT_COUNT=0 + FILE_COUNT=0 + mkdir -p spv + mkdir -p wgsl + + echo "==== Validate spv and generate wgsl ====" + rm -f counter + touch counter + + find "./spv" -name '*.spv' | while read fname; + do + echo "Convert: $fname" + FILE_COUNT=$((FILE_COUNT+1)) + ../../target/release/naga --validate 27 $(realpath ${fname}) ./wgsl/$(basename ${fname}).wgsl + if [[ $? -eq 0 ]]; then + SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1)) + fi + echo "Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT" > counter + done + cat counter - name: Validate output `wgsl` run: | - set +e - cd naga/spirv-samples - SUCCESS_RESULT_COUNT=0 - FILE_COUNT=0 - - rm -f counter - touch counter - - find "./wgsl" -name '*.wgsl' | while read fname; - do - echo "Validate: $fname" - FILE_COUNT=$((FILE_COUNT+1)) - ../../target/release/naga --validate 27 $(realpath ${fname}) - if [[ $? -eq 0 ]]; then - SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1)) - fi - echo "Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT" > counter - done - cat counter + set +e + cd naga/spirv-samples + SUCCESS_RESULT_COUNT=0 + FILE_COUNT=0 + + rm -f counter + touch counter + + find "./wgsl" -name '*.wgsl' | while read fname; + do + echo "Validate: $fname" + FILE_COUNT=$((FILE_COUNT+1)) + ../../target/release/naga --validate 27 $(realpath ${fname}) + if [[ $? -eq 0 ]]; then + SUCCESS_RESULT_COUNT=$((SUCCESS_RESULT_COUNT + 1)) + fi + echo "Result: $(expr $FILE_COUNT - $SUCCESS_RESULT_COUNT) / $FILE_COUNT" > counter + done + cat counter diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 44326879b16..1a4385da0ac 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -3,7 +3,7 @@ name: Publish on: pull_request: paths: - - '.github/workflows/publish.yml' + - ".github/workflows/publish.yml" push: branches: - trunk @@ -13,12 +13,19 @@ env: CARGO_TERM_COLOR: always RUST_BACKTRACE: full +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} + jobs: publish: runs-on: ubuntu-latest steps: - name: Checkout repo - uses: actions/checkout@v4 + uses: actions/checkout@v5 with: persist-credentials: false diff --git a/.github/workflows/shaders.yml b/.github/workflows/shaders.yml index 8bcd6451506..062d6ecb142 100644 --- a/.github/workflows/shaders.yml +++ b/.github/workflows/shaders.yml @@ -3,27 +3,29 @@ name: Shaders on: push: branches-ignore: [ - # We don't need to run on renovate PRs. + # Renovate branches are always PRs, so they will be covered + # by the pull_request event. "renovate/**", - # This is the branch the merge queue creates. + # Branches with the `gh-readonly-queue` prefix are used by the + # merge queue, so they are already covered by the `merge_group` event. "gh-readonly-queue/**", ] - tags: [v0.*] pull_request: merge_group: -env: - # Sourced from https://vulkan.lunarg.com/sdk/home#linux - # - # We don't include the 4th version number, as it's not used in any URL. - VULKAN_SDK_VERSION: "1.4.313" +# Every time a PR is pushed to, cancel any previous jobs. This +# makes us behave nicer to github and get faster turnaround times +# on PRs that are pushed to multiple times in rapid succession. +concurrency: + group: ${{github.workflow}}-${{github.ref}} + cancel-in-progress: ${{github.event_name == 'pull_request'}} jobs: naga-validate-windows: name: "Validate: HLSL" runs-on: windows-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Debug symbols to `line-tables-only` shell: bash @@ -65,9 +67,9 @@ jobs: naga-validate-macos: name: "Validate: MSL" - runs-on: macos-14 + runs-on: macos-15 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Debug symbols to line-tables-only shell: bash @@ -89,21 +91,10 @@ jobs: name: "Validate: SPIR-V/GLSL/DOT/WGSL" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v5 - name: Install Vulkan SDK - shell: bash - run: | - set -e - - sudo apt-get update -y -qq - - # vulkan sdk - wget -qO - https://packages.lunarg.com/lunarg-signing-key-pub.asc | sudo apt-key add - - sudo wget -qO /etc/apt/sources.list.d/lunarg-vulkan-$VULKAN_SDK_VERSION-noble.list https://packages.lunarg.com/vulkan/$VULKAN_SDK_VERSION/lunarg-vulkan-$VULKAN_SDK_VERSION-noble.list - - sudo apt-get update - sudo apt install -y vulkan-sdk + uses: ./.github/actions/install-vulkan-sdk - name: Install Graphviz run: sudo apt-get install graphviz diff --git a/.gitignore b/.gitignore index f92fb07f943..a23fffc26db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ # Jujutsu # -# When using the WGPU repository through [Jujutsu](https://github.com/martinvonz/jj), `.jj/` dirs. +# When using the wgpu repository through [Jujutsu](https://github.com/martinvonz/jj), `.jj/` dirs. # are ignored because Jujutsu writes a `.jj/.gitignore` file containing `/*`. Some tools, like # `prettier`, don't handle nested `.gitgnore` properly, but they _do_ handle top-level `.gitignore` # patterns properly. So, include it here, even though we shouldn't need it. :cry: diff --git a/CHANGELOG.md b/CHANGELOG.md index 82d6698c010..ed9adec5b2c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -27,7 +27,7 @@ Top level categories: Bottom level categories: -- Naga +- naga - General - DX12 - Vulkan @@ -40,6 +40,333 @@ Bottom level categories: ## Unreleased +## v27.0.4 (2025-10-23) + +This release includes `wgpu-hal` version `27.0.4`. All other crates remain at their previous versions. + +### Bug Fixes + +#### Vulkan + +- Work around extremely poor frame pacing from AMD and Nvidia cards on Windows in `Fifo` and `FifoRelaxed` present modes. This is due to the drivers implicitly using a DXGI (Direct3D) swapchain to implement these modes and it having vastly different timing properties. See https://github.com/gfx-rs/wgpu/issues/8310 and https://github.com/gfx-rs/wgpu/issues/8354 for more information. By @cwfitzgerald in [#8420](https://github.com/gfx-rs/wgpu/pull/8420). + +## v27.0.3 (2025-10-22) + +This release includes `naga`, `wgpu-core` and `wgpu-hal` version `27.0.3`. All other crates remain at their previous versions. + +### Bug Fixes + +#### naga + +- Fix a bug that resulted in the Metal error `program scope variable must reside in constant address space` in some cases. Backport of [#8311](https://github.com/gfx-rs/wgpu/pull/8311) by @teoxoy. + +#### General + +- Remove an assertion that causes problems if `CommandEncoder::as_hal_mut` is used. By @andyleiserson in [#8387](https://github.com/gfx-rs/wgpu/pull/8387). + +#### DX12 + +- Align copies b/w textures and buffers via a single intermediate buffer per copy when `D3D12_FEATURE_DATA_D3D12_OPTIONS13.UnrestrictedBufferTextureCopyPitchSupported` is `false`. By @ErichDonGubler in [#7721](https://github.com/gfx-rs/wgpu/pull/7721), backported in [#8374](https://github.com/gfx-rs/wgpu/pull/8374). + +## v27.0.2 (2025-10-03) + +### Bug Fixes + +#### DX12 + +- Fix device creation failures for devices that do not support mesh shaders. By @vorporeal in [#8297](https://github.com/gfx-rs/wgpu/pull/8297). + +## v27.0.1 (2025-10-02) + +### Bug Fixes + +- Fixed the build on docs.rs. By @cwfitzgerald in [#8292](https://github.com/gfx-rs/wgpu/pull/8292). + +## v27.0.0 (2025-10-01) + +### Major Changes + +#### Deferred command buffer actions: `map_buffer_on_submit` and `on_submitted_work_done` + +You may schedule buffer mapping and a submission-complete callback to run automatically after you submit, directly from encoders, command buffers, and passes. + +```rust +// Record some GPU work so the submission isn't empty and touches `buffer`. +encoder.clear_buffer(&buffer, 0, None); + +// Defer mapping until this encoder is submitted. +encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..size, |result| { .. }); + +// Fires after the command buffer's work is finished. +encoder.on_submitted_work_done(|| { .. }); + +// Automatically calls `map_async` and `on_submitted_work_done` after this submission finishes. +queue.submit([encoder.finish()]); +``` + +Available on `CommandEncoder`, `CommandBuffer`, `RenderPass`, and `ComputePass`. + +By @cwfitzgerald in [#8125](https://github.com/gfx-rs/wgpu/pull/8125). + +#### Builtin Support for DXGI swapchains on top of of DirectComposition Visuals in DX12 + +By enabling DirectComposition support, the dx12 backend can now support transparent windows. + +This creates a single `IDCompositionVisual` over the entire window that is used by the mf`Surface`. If a user wants to manage the composition tree themselves, they should create their own device and composition, and pass the relevant visual down into `wgpu` via `SurfaceTargetUnsafe::CompositionVisual`. + +```rust +let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { + backend_options: wgpu::BackendOptions { + dx12: wgpu::Dx12BackendOptions { + presentation_system: wgpu::Dx12SwapchainKind::DxgiFromVisual, + .. + }, + .. + }, + .. +}); +``` + +By @n1ght-hunter in [#7550](https://github.com/gfx-rs/wgpu/pull/7550). + +#### `EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE` has been merged into `EXPERIMENTAL_RAY_QUERY` + +We have merged the acceleration structure feature into the `RayQuery` feature. This is to help work around an AMD driver bug and reduce the feature complexity of ray tracing. In the future when ray tracing pipelines are implemented, if either feature is enabled, acceleration structures will be available. + +```diff +- Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE ++ Features::EXPERIMENTAL_RAY_QUERY +``` + +By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913). + +#### New `EXPERIMENTAL_PRECOMPILED_SHADERS` API + +We have added `Features::EXPERIMENTAL_PRECOMPILED_SHADERS`, replacing existing passthrough types with a unified `CreateShaderModuleDescriptorPassthrough` which allows passing multiple shader codes for different backends. By @SupaMaggie70Incorporated in [#7834](https://github.com/gfx-rs/wgpu/pull/7834) + +Difference for SPIR-V passthrough: + +```diff +- device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough::SpirV( +- wgpu::ShaderModuleDescriptorSpirV { +- label: None, +- source: spirv_code, +- }, +- )) ++ device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { ++ entry_point: "main".into(), ++ label: None, ++ spirv: Some(spirv_code), ++ ..Default::default() +}) +``` + +This allows using precompiled shaders without manually checking which backend's code to pass, for example if you have shaders precompiled for both DXIL and SPIR-V. + +#### Buffer mapping apis no longer have lifetimes + +`Buffer::get_mapped_range()`, `Buffer::get_mapped_range_mut()`, and `Queue::write_buffer_with()` now return guard objects without any lifetimes. This +makes it significantly easier to store these types in structs, which is useful for building utilities that build the contents of a buffer over time. + +```diff +- let buffer_mapping_ref: wgpu::BufferView<'_> = buffer.get_mapped_range(..); +- let buffer_mapping_mut: wgpu::BufferViewMut<'_> = buffer.get_mapped_range_mut(..); +- let queue_write_with: wgpu::QueueWriteBufferView<'_> = queue.write_buffer_with(..); ++ let buffer_mapping_ref: wgpu::BufferView = buffer.get_mapped_range(..); ++ let buffer_mapping_mut: wgpu::BufferViewMut = buffer.get_mapped_range_mut(..); ++ let queue_write_with: wgpu::QueueWriteBufferView = queue.write_buffer_with(..); +``` + +By @sagudev in [#8046](https://github.com/gfx-rs/wgpu/pull/8046) and @cwfitzgerald in [#8070](https://github.com/gfx-rs/wgpu/pull/8161). + +#### `EXPERIMENTAL_*` features now require unsafe code to enable + +We want to be able to expose potentially experimental features to our users before we have ensured that they are fully sound to use. +As such, we now require any feature that is prefixed with `EXPERIMENTAL` to have a special unsafe token enabled in the device descriptor +acknowledging that the features may still have bugs in them and to report any they find. + +```rust +adapter.request_device(&wgpu::DeviceDescriptor { + features: wgpu::Features::EXPERIMENTAL_MESH_SHADER, + experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() } + .. +}) +``` + +By @cwfitzgerald in [#8163](https://github.com/gfx-rs/wgpu/pull/8163). + +#### Multi-draw indirect is now unconditionally supported when indirect draws are supported + +We have removed `Features::MULTI_DRAW_INDIRECT` as it was unconditionally available on all platforms. +`RenderPass::multi_draw_indirect` is now available if the device supports downlevel flag `DownlevelFlags::INDIRECT_EXECUTION`. + +If you are using spirv-passthrough with multi-draw indirect and `gl_DrawID`, you can know if `MULTI_DRAW_INDIRECT` is being emulated +by if the `Feature::MULTI_DRAW_INDIRECT_COUNT` feature is available on the device, this feature cannot be emulated efficicently. + +By @cwfitzgerald in [#8162](https://github.com/gfx-rs/wgpu/pull/8162). + +#### `wgpu::PollType::Wait` has now an optional timeout + +We removed `wgpu::PollType::WaitForSubmissionIndex` and added fields to `wgpu::PollType::Wait` in order to express timeouts. + +Before/after for `wgpu::PollType::Wait`: + +```diff +-device.poll(wgpu::PollType::Wait).unwrap(); +-device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); ++device.poll(wgpu::PollType::Wait { ++ submission_index: None, // Wait for most recent submission ++ timeout: Some(std::time::Duration::from_secs(60)), // Previous behavior, but more likely you want `None` instead. ++ }) ++ .unwrap(); +``` + +Before/after for `wgpu::PollType::WaitForSubmissionIndex`: + +```diff +-device.poll(wgpu::PollType::WaitForSubmissionIndex(index_to_wait_on)) ++device.poll(wgpu::PollType::Wait { ++ submission_index: Some(index_to_wait_on), ++ timeout: Some(std::time::Duration::from_secs(60)), // Previous behavior, but more likely you want `None` instead. ++ }) ++ .unwrap(); +``` + +⚠️ Previously, both `wgpu::PollType::WaitForSubmissionIndex` and `wgpu::PollType::Wait` had a hard-coded timeout of 60 seconds. + +To wait indefinitely on the latest submission, you can also use the `wait_indefinitely` convenience function: + +```rust +device.poll(wgpu::PollType::wait_indefinitely()); +``` + +By @wumpf in [#8282](https://github.com/gfx-rs/wgpu/pull/8282), [#8285](https://github.com/gfx-rs/wgpu/pull/8285) + +### New Features + +#### General + +- Added mesh shader support to `wgpu`, with examples. Requires passthrough. By @SupaMaggie70Incorporated in [#7345](https://github.com/gfx-rs/wgpu/pull/7345). +- Added support for external textures based on WebGPU's [`GPUExternalTexture`](https://www.w3.org/TR/webgpu/#gpuexternaltexture). These allow shaders to transparently operate on potentially multiplanar source texture data in either RGB or YCbCr formats via WGSL's `texture_external` type. This is gated behind the `Features::EXTERNAL_TEXTURE` feature, which is currently only supported on DX12. By @jamienicol in [#4386](https://github.com/gfx-rs/wgpu/issues/4386). +- `wgpu::Device::poll` can now specify a timeout via `wgpu::PollType::Wait`. By @wumpf in [#8282](https://github.com/gfx-rs/wgpu/pull/8282) & [#8285](https://github.com/gfx-rs/wgpu/pull/8285) + +#### naga + +- Expose `naga::front::wgsl::UnimplementedEnableExtension`. By @ErichDonGubler in [#8237](https://github.com/gfx-rs/wgpu/pull/8237). + +### Changes + +#### General + +- Command encoding now happens when `CommandEncoder::finish` is called, not when the individual operations are requested. This does not affect the API, but may affect performance characteristics. By @andyleiserson in [#8220](https://github.com/gfx-rs/wgpu/pull/8220). +- Prevent resources for acceleration structures being created if acceleration structures are not enabled. By @Vecvec in [#8036](https://github.com/gfx-rs/wgpu/pull/8036). +- Validate that each `push_debug_group` pairs with exactly one `pop_debug_group`. By @andyleiserson in [#8048](https://github.com/gfx-rs/wgpu/pull/8048). +- `set_viewport` now requires that the supplied minimum depth value is less than the maximum depth value. By @andyleiserson in [#8040](https://github.com/gfx-rs/wgpu/pull/8040). +- Validation of `copy_texture_to_buffer`, `copy_buffer_to_texture`, and `copy_texture_to_texture` operations more closely follows the WebGPU specification. By @andyleiserson in various PRs. + - Copies within the same texture must not overlap. + - Copies of multisampled or depth/stencil formats must span an entire subresource (layer). + - Copies of depth/stencil formats must be 4B aligned. + - For texture-buffer copies, `bytes_per_row` on the buffer side must be 256B-aligned, even if the transfer is a single row. +- The offset for `set_vertex_buffer` and `set_index_buffer` must be 4B aligned. By @andyleiserson in [#7929](https://github.com/gfx-rs/wgpu/pull/7929). +- The offset and size of bindings are validated as fitting within the underlying buffer in more cases. By @andyleiserson in [#7911](https://github.com/gfx-rs/wgpu/pull/7911). +- The function you pass to `Device::on_uncaptured_error()` must now implement `Sync` in addition to `Send`, and be wrapped in `Arc` instead of `Box`. + In exchange for this, it is no longer possible for calling `wgpu` functions while in that callback to cause a deadlock (not that we encourage you to actually do that). + By @kpreid in [#8011](https://github.com/gfx-rs/wgpu/pull/8011). +- Make a compacted hal acceleration structure inherit a label from the base BLAS. By @Vecvec in [#8103](https://github.com/gfx-rs/wgpu/pull/8103). +- The limits requested for a device must now satisfy `min_subgroup_size <= max_subgroup_size`. By @andyleiserson in [#8085](https://github.com/gfx-rs/wgpu/pull/8085). +- Improve errors when buffer mapping is done incorrectly. Allow aliasing immutable [`BufferViews`]. By @cwfitzgerald in [#8150](https://github.com/gfx-rs/wgpu/pull/8150). +- Require new `F16_IN_F32` downlevel flag for `quantizeToF16`, `pack2x16float`, and `unpack2x16float` in WGSL input. By @aleiserson in [#8130](https://github.com/gfx-rs/wgpu/pull/8130). +- The error message for non-copyable depth/stencil formats no longer mentions the aspect when it is not relevant. By @reima in [#8156](https://github.com/gfx-rs/wgpu/pull/8156). +- Track the initialization status of buffer memory correctly when `copy_texture_to_buffer` skips over padding space between rows or layers, or when the start/end of a texture-buffer transfer is not 4B aligned. By @andyleiserson in [#8099](https://github.com/gfx-rs/wgpu/pull/8099). + +#### naga + +- naga now requires that no type be larger than 1 GB. This limit may be lowered in the future; feedback on an appropriate value for the limit is welcome. By @andyleiserson in [#7950](https://github.com/gfx-rs/wgpu/pull/7950). +- If the shader source contains control characters, naga now replaces them with U+FFFD ("replacement character") in diagnostic output. By @andyleiserson in [#8049](https://github.com/gfx-rs/wgpu/pull/8049). +- Add f16 IO polyfill on Vulkan backend to enable SHADER_F16 use without requiring `storageInputOutput16`. By @cryvosh in [#7884](https://github.com/gfx-rs/wgpu/pull/7884). +- For custom Naga backend authors: `naga::proc::Namer` now accepts reserved keywords using two new dedicated types, `proc::{KeywordSet, CaseInsensitiveKeywordSet}`. By @kpreid in [#8136](https://github.com/gfx-rs/wgpu/pull/8136). +- **BREAKING**: Previously the WGSL storage-texture format `rg11b10float` was incorrectly accepted and generated by naga, but now only accepts the the correct name `rg11b10ufloat` instead. By @ErikWDev in [#8219](https://github.com/gfx-rs/wgpu/pull/8219). +- The [`source()`](https://doc.rust-lang.org/std/error/trait.Error.html#method.source) method of `ShaderError` no longer reports the error as its own source. By @andyleiserson in [#8258](https://github.com/gfx-rs/wgpu/pull/8258). +- naga correctly ingests SPIR-V that use descriptor runtime indexing, which in turn is correctly converted into WGSLs binding array. By @hasenbanck in [8256](https://github.com/gfx-rs/wgpu/pull/8256). +- naga correctly ingests SPIR-V that loads from multi-sampled textures, which in turn is correctly converted into WGSLs texture_multisampled_2d and load operations. By @hasenbanck in [8270](https://github.com/gfx-rs/wgpu/pull/8270). +- naga implement OpImageGather and OpImageDrefGather operations when ingesting SPIR-V. By @hasenbanck in [8280](https://github.com/gfx-rs/wgpu/pull/8280). + +#### DX12 + +- Allow disabling waiting for latency waitable object. By @marcpabst in [#7400](https://github.com/gfx-rs/wgpu/pull/7400) +- Add mesh shader support, including to the example. By @SupaMaggie70Incorporated in [#8110](https://github.com/gfx-rs/wgpu/issues/8110) + +### Bug Fixes + +#### General + +- Validate that effective buffer binding size is aligned to 4 when creating bind groups with buffer entries.. By @ErichDonGubler in [8041](https://github.com/gfx-rs/wgpu/pull/8041). + +#### DX12 + +- Create an event per wait to prevent 60 second hangs in certain multithreaded scenarios. By @Vecvec in [#8273](https://github.com/gfx-rs/wgpu/pull/8273). +- Fixed a bug where access to matrices with 2 rows would not work in some cases. By @andyleiserson in [#7438](https://github.com/gfx-rs/wgpu/pull/7438). + +##### EGL + +- Fixed unwrap failed in context creation for some Android devices. By @uael in [#8024](https://github.com/gfx-rs/wgpu/pull/8024). + +##### Vulkan + +- Fixed wrong color format+space being reported versus what is hardcoded in `create_swapchain()`. By @MarijnS95 in [#8226](https://github.com/gfx-rs/wgpu/pull/8226). + +#### naga + +- [wgsl-in] Allow a trailing comma in `@blend_src(…)` attributes. By @ErichDonGubler in [#8137](https://github.com/gfx-rs/wgpu/pull/8137). +- [wgsl-in] Allow a trailing comma in the list of `case` values inside a `switch`. By @reima in [#8165](https://github.com/gfx-rs/wgpu/pull/8165). +- Escape, rather than strip, identifiers with Unicode. By @ErichDonGubler in [7995](https://github.com/gfx-rs/wgpu/pull/7995). + +### Documentation + +#### General + +- Clarify that subgroup barriers require both the `SUBGROUP` and `SUBGROUP_BARRIER` features / capabilities. By @andyleiserson in [#8203](https://github.com/gfx-rs/wgpu/pull/8203). + +## v26.0.4 (2025-08-07) + +### Bug Fixes + +#### Vulkan + +- Fix `STATUS_HEAP_CORRUPTION` crash when concurrently calling `create_sampler`. By @atlv24 in [#8043](https://github.com/gfx-rs/wgpu/pull/8043), [#8056](https://github.com/gfx-rs/wgpu/pull/8056). + +## v26.0.3 (2025-07-30) + +### Bug Fixes + +- Fixed memory leak in vulkan backend. By @cwfitzgerald in [#8031](https://github.com/gfx-rs/wgpu/pull/8031). + +### Bug Fixes + +#### naga + +- Fix empty `if` statements causing errors on spirv 1.6+. By @Vecvec in [#7883](https://github.com/gfx-rs/wgpu/pull/7883). + +## v26.0.2 (2025-07-23) + +### Bug Fixes + +- Fixed vulkan validation error regarding the swapchain in latest SDK. By @cwfitzgerald in [#7971](https://github.com/gfx-rs/wgpu/pull/7971). +- Fixed flickering on AMD devices and crashes inside Renderdoc due to incorrect caching of `VkFramebuffer`s when the driver re-used image view handles. By @cwfitzgerald in [#7972](https://github.com/gfx-rs/wgpu/pull/7972). + +> [!WARNING] +> There is formally a breaking change in `wgpu_hal::vulkan::Device::texture_from_raw` as there is now a `&self` receiver where +> there previously wasn't one. This will not affect you unless you explicitly use this api. We have gone ahead with the release +> as the bug was pervasive and made wgpu unusable for the affected people on v26. + +## v26.0.1 (2025-07-10) + +### Bug Fixes + +- Fixed build error inside `wgpu::util::initialize_adapter_from_env` when `std` feature is not enabled. By @kpreid in [#7918](https://github.com/gfx-rs/wgpu/pull/7918). +- Fixed build error occurring when the `profiling` dependency is configured to have profiling active. By @kpreid in [#7916](https://github.com/gfx-rs/wgpu/pull/7916). +- Emit a validation error instead of panicking when a query set index is OOB. By @ErichDonGubler in [#7908](https://github.com/gfx-rs/wgpu/pull/7908). + ## v26.0.0 (2025-07-09) ### Major Features @@ -97,11 +424,9 @@ let (device, queue) = adapter .unwrap(); ``` -More examples of this +By @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829). -By @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829). - -### Naga +### naga - Added `no_std` support with default features disabled. By @Bushrat011899 in [#7585](https://github.com/gfx-rs/wgpu/pull/7585). - [wgsl-in,ir] Add support for parsing rust-style doc comments via `naga::front::glsl::Frontend::new_with_options`. By @Vrixyz in [#6364](https://github.com/gfx-rs/wgpu/pull/6364). @@ -118,7 +443,7 @@ By @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829). - Add extra acceleration structure vertex formats. By @Vecvec in [#7580](https://github.com/gfx-rs/wgpu/pull/7580). - Add acceleration structure limits. By @Vecvec in [#7845](https://github.com/gfx-rs/wgpu/pull/7845). - Add support for clip-distances feature for Vulkan and GL backends. By @dzamkov in [#7730](https://github.com/gfx-rs/wgpu/pull/7730) -- Added `wgpu_types::error::{ErrorType, WebGpuError}` for classification of errors according to WebGPU's [`GPUError`]'s classification scheme, and implement `WebGpuError` for existing errors. This allows users of `wgpu-core` to offload error classification onto the WGPU ecosystem, rather than having to do it themselves without sufficient information. By @ErichDonGubler in [#6547](https://github.com/gfx-rs/wgpu/pull/6547). +- Added `wgpu_types::error::{ErrorType, WebGpuError}` for classification of errors according to WebGPU's [`GPUError`]'s classification scheme, and implement `WebGpuError` for existing errors. This allows users of `wgpu-core` to offload error classification onto the wgpu ecosystem, rather than having to do it themselves without sufficient information. By @ErichDonGubler in [#6547](https://github.com/gfx-rs/wgpu/pull/6547). [`GPUError`]: https://www.w3.org/TR/webgpu/#gpuerror @@ -129,10 +454,10 @@ By @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829). - Fix error message for sampler array limit. By @LPGhatguy in [#7704](https://github.com/gfx-rs/wgpu/pull/7704). - Fix bug where using `BufferSlice::get_mapped_range_as_array_buffer()` on a buffer would prevent you from ever unmapping it. Note that this API has changed and is now `BufferView::as_uint8array()`. -#### Naga +#### naga -- Naga now infers the correct binding layout when a resource appears only in an assignment to `_`. By @andyleiserson in [#7540](https://github.com/gfx-rs/wgpu/pull/7540). -- Implement `dot4U8Packed` and `dot4I8Packed` for all backends, using specialized intrinsics on SPIR-V, HSLS, and Metal if available, and polyfills everywhere else. By @robamler in [#7494](https://github.com/gfx-rs/wgpu/pull/7494), [#7574](https://github.com/gfx-rs/wgpu/pull/7574), and [#7653](https://github.com/gfx-rs/wgpu/pull/7653). +- naga now infers the correct binding layout when a resource appears only in an assignment to `_`. By @andyleiserson in [#7540](https://github.com/gfx-rs/wgpu/pull/7540). +- Implement `dot4U8Packed` and `dot4I8Packed` for all backends, using specialized intrinsics on SPIR-V, HLSL, and Metal if available, and polyfills everywhere else. By @robamler in [#7494](https://github.com/gfx-rs/wgpu/pull/7494), [#7574](https://github.com/gfx-rs/wgpu/pull/7574), and [#7653](https://github.com/gfx-rs/wgpu/pull/7653). - Add polyfilled `pack4x{I,U}8Clamped` built-ins to all backends and WGSL frontend. By @ErichDonGubler in [#7546](https://github.com/gfx-rs/wgpu/pull/7546). - Allow textureLoad's sample index arg to be unsigned. By @jimblandy in [#7625](https://github.com/gfx-rs/wgpu/pull/7625). - Properly convert arguments to atomic operations. By @jimblandy in [#7573](https://github.com/gfx-rs/wgpu/pull/7573). @@ -182,8 +507,7 @@ By @Vecvec in [#7829](https://github.com/gfx-rs/wgpu/pull/7829). - The definition of `enum CommandEncoderError` has changed significantly, to reflect which errors can be raised by `CommandEncoder.finish()`. There are also some errors that no longer appear directly in `CommandEncoderError`, and instead appear nested within the `RenderPass` or `ComputePass` variants. - `CopyError` has been removed. Errors that were previously a `CopyError` are now a `CommandEncoderError` returned by `finish()`. (The detailed reasons for copies to fail were and still are described by `TransferError`, which was previously a variant of `CopyError`, and is now a variant of `CommandEncoderError`). - -#### Naga +#### naga - Mark `readonly_and_readwrite_storage_textures` & `packed_4x8_integer_dot_product` language extensions as implemented. By @teoxoy in [#7543](https://github.com/gfx-rs/wgpu/pull/7543) - `naga::back::hlsl::Writer::new` has a new `pipeline_options` argument. `hlsl::PipelineOptions::default()` can be passed as a default. The `shader_stage` and `entry_point` members of `pipeline_options` can be used to write only a single entry point when using the HLSL and MSL backends (GLSL and SPIR-V already had this functionality). The Metal and DX12 HALs now write only a single entry point when loading shaders. By @andyleiserson in [#7626](https://github.com/gfx-rs/wgpu/pull/7626). @@ -338,7 +662,7 @@ There is now documentation to describe how this maps to the various debuggers' a By @cwfitzgerald in [#7470](https://github.com/gfx-rs/wgpu/pull/7470) -##### Ensure loops generated by SPIR-V and HLSL Naga backends are bounded +##### Ensure loops generated by SPIR-V and HLSL naga backends are bounded Make sure that all loops in shaders generated by these naga backends are bounded to avoid undefined behaviour due to infinite loops. Note that this may have a @@ -465,7 +789,7 @@ By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](h - new `Features::MSL_SHADER_PASSTHROUGH` run-time feature allows providing pass-through MSL Metal shaders. By @syl20bnr in [#7326](https://github.com/gfx-rs/wgpu/pull/7326). - Added mesh shader support to `wgpu_hal`. By @SupaMaggie70Incorporated in [#7089](https://github.com/gfx-rs/wgpu/pull/7089) -#### Naga +#### naga - Add support for unsigned types when calling textureLoad with the level parameter. By @ygdrasil-io in [#7058](https://github.com/gfx-rs/wgpu/pull/7058). - Support @must_use attribute on function declarations. By @turbocrime in [#6801](https://github.com/gfx-rs/wgpu/pull/6801). @@ -490,15 +814,15 @@ By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](h - Rename `instance_id` and `instance_custom_index` to `instance_index` and `instance_custom_data` by @Vecvec in [#6780](https://github.com/gfx-rs/wgpu/pull/6780) -#### Naga +#### naga -- Naga IR types are now available in the module `naga::ir` (e.g. `naga::ir::Module`). +- naga IR types are now available in the module `naga::ir` (e.g. `naga::ir::Module`). The original names (e.g. `naga::Module`) remain present for compatibility. By @kpreid in [#7365](https://github.com/gfx-rs/wgpu/pull/7365). - Refactored `use` statements to simplify future `no_std` support. By @bushrat011899 in [#7256](https://github.com/gfx-rs/wgpu/pull/7256) -- Naga's WGSL frontend no longer allows using the `&` operator to take the address of a component of a vector, +- naga's WGSL frontend no longer allows using the `&` operator to take the address of a component of a vector, which is not permitted by the WGSL specification. By @andyleiserson in [#7284](https://github.com/gfx-rs/wgpu/pull/7284) -- Naga's use of `termcolor` and `stderr` are now optional behind features of the same names. By @bushrat011899 in [#7482](https://github.com/gfx-rs/wgpu/pull/7482) +- naga's use of `termcolor` and `stderr` are now optional behind features of the same names. By @bushrat011899 in [#7482](https://github.com/gfx-rs/wgpu/pull/7482) #### Vulkan @@ -508,7 +832,7 @@ By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](h ### Bug Fixes -#### Naga +#### naga - Fix some instances of functions which have a return type but don't return a value being incorrectly validated. By @jamienicol in [#7013](https://github.com/gfx-rs/wgpu/pull/7013). - Allow abstract expressions to be used in WGSL function return statements. By @jamienicol in [#7035](https://github.com/gfx-rs/wgpu/pull/7035). @@ -540,7 +864,7 @@ By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](h #### Vulkan - Stop naga causing undefined behavior when a ray query misses. By @Vecvec in [#6752](https://github.com/gfx-rs/wgpu/pull/6752). -- In Naga's SPIR-V backend, avoid duplicating SPIR-V OpTypePointer instructions. By @jimblandy in [#7246](https://github.com/gfx-rs/wgpu/pull/7246). +- In naga's SPIR-V backend, avoid duplicating SPIR-V OpTypePointer instructions. By @jimblandy in [#7246](https://github.com/gfx-rs/wgpu/pull/7246). #### Gles @@ -560,7 +884,7 @@ By @cwfitzgerald in [#6811](https://github.com/gfx-rs/wgpu/pull/6811), [#6815](h ### Performance -#### Naga +#### naga - Replace `unicode-xid` with `unicode-ident`. By @CrazyboyQCD in [#7135](https://github.com/gfx-rs/wgpu/pull/7135) @@ -733,7 +1057,7 @@ By @cwfitzgerald in [#6895](https://github.com/gfx-rs/wgpu/pull/6895) #### The `diagnostic(…);` directive is now supported in WGSL -Naga now parses `diagnostic(…);` directives according to the WGSL spec. This allows users to control certain lints, similar to Rust's `allow`, `warn`, and `deny` attributes. For example, in standard WGSL (but, notably, not Naga yet—see ) this snippet would emit a uniformity error: +naga now parses `diagnostic(…);` directives according to the WGSL spec. This allows users to control certain lints, similar to Rust's `allow`, `warn`, and `deny` attributes. For example, in standard WGSL (but, notably, not naga yet—see ) this snippet would emit a uniformity error: ```wgsl @group(0) @binding(0) var s : sampler; @@ -775,15 +1099,15 @@ fn main(@builtin(position) p : vec4f) -> @location(0) vec4f { There are some limitations to keep in mind with this new functionality: - We support `@diagnostic(…)` rules as `fn` attributes, but prioritization for rules in statement positions (i.e., `if (…) @diagnostic(…) { … }` is unclear. If you are blocked by not being able to parse `diagnostic(…)` rules in statement positions, please let us know in , so we can determine how to prioritize it! -- Standard WGSL specifies `error`, `warning`, `info`, and `off` severity levels. These are all technically usable now! A caveat, though: warning- and info-level are only emitted to `stderr` via the `log` façade, rather than being reported through a `Result::Err` in Naga or the `CompilationInfo` interface in `wgpu{,-core}`. This will require breaking changes in Naga to fix, and is being tracked by . -- Not all lints can be controlled with `diagnostic(…)` rules. In fact, only the `derivative_uniformity` triggering rule exists in the WGSL standard. That said, Naga contributors are excited to see how this level of control unlocks a new ecosystem of configurable diagnostics. +- Standard WGSL specifies `error`, `warning`, `info`, and `off` severity levels. These are all technically usable now! A caveat, though: warning- and info-level are only emitted to `stderr` via the `log` façade, rather than being reported through a `Result::Err` in naga or the `CompilationInfo` interface in `wgpu{,-core}`. This will require breaking changes in naga to fix, and is being tracked by . +- Not all lints can be controlled with `diagnostic(…)` rules. In fact, only the `derivative_uniformity` triggering rule exists in the WGSL standard. That said, naga contributors are excited to see how this level of control unlocks a new ecosystem of configurable diagnostics. - Finally, `diagnostic(…)` rules are not yet emitted in WGSL output. This means that `wgsl-in` → `wgsl-out` is currently a lossy process. We felt that it was important to unblock users who needed `diagnostic(…)` rules (i.e., ) before we took significant effort to fix this (tracked in ). By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456), [#6148](https://github.com/gfx-rs/wgpu/pull/6148), [#6533](https://github.com/gfx-rs/wgpu/pull/6533), [#6353](https://github.com/gfx-rs/wgpu/pull/6353), [#6537](https://github.com/gfx-rs/wgpu/pull/6537). #### New Features -##### Naga +##### naga - Support atomic operations on fields of global structs in the SPIR-V frontend. By @schell in [#6693](https://github.com/gfx-rs/wgpu/pull/6693). - Clean up tests for atomic operations support in SPIR-V frontend. By @schell in [#6692](https://github.com/gfx-rs/wgpu/pull/6692) @@ -829,7 +1153,7 @@ By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456), [#6148] #### Changes -##### Naga +##### naga - Show types of LHS and RHS in binary operation type mismatch errors. By @ErichDonGubler in [#6450](https://github.com/gfx-rs/wgpu/pull/6450). - The GLSL parser now uses less expressions for function calls. By @magcius in [#6604](https://github.com/gfx-rs/wgpu/pull/6604). @@ -882,7 +1206,7 @@ By @ErichDonGubler in [#6456](https://github.com/gfx-rs/wgpu/pull/6456), [#6148] - Fix `wgpu-info` not showing dx12 adapters. By @wumpf in [#6844](https://github.com/gfx-rs/wgpu/pull/6844). - Use `transform_buffer_offset` when initialising `transform_buffer`. By @Vecvec in [#6864](https://github.com/gfx-rs/wgpu/pull/6864). -#### Naga +#### naga - Fix crash when a texture argument is missing. By @aedm in [#6486](https://github.com/gfx-rs/wgpu/pull/6486) - Emit an error in constant evaluation, rather than crash, in certain cases where `vecN` constructors have less than N arguments. By @ErichDonGubler in [#6508](https://github.com/gfx-rs/wgpu/pull/6508). @@ -936,7 +1260,7 @@ Below changes were cherry-picked from 24.0.0 development line. ### Themes of this release -This release's theme is one that is likely to repeat for a few releases: convergence with the WebGPU specification! WGPU's design and base functionality are actually determined by two specifications: one for WebGPU, and one for the WebGPU Shading Language. +This release's theme is one that is likely to repeat for a few releases: convergence with the WebGPU specification! wgpu's design and base functionality are actually determined by two specifications: one for WebGPU, and one for the WebGPU Shading Language. This may not sound exciting, but let us convince you otherwise! All major web browsers have committed to offering WebGPU in their environment. Even JS runtimes like [Node][nodejs-webgpu-interest] and [Deno][deno_webgpu-crate-manifest] have communities that are very interested in providing WebGPU! WebGPU is slowly [eating the world][eat-the-world-meaning], as it were. 😀 It's really important, then, that WebGPU implementations behave in ways that one would expect across all platforms. For example, if Firefox's WebGPU implementation were to break when running scripts and shaders that worked just fine in Chrome, that would mean sad users for both application authors _and_ browser authors. @@ -944,23 +1268,23 @@ This may not sound exciting, but let us convince you otherwise! All major web br [deno_webgpu-crate-manifest]: https://github.com/gfx-rs/wgpu/tree/64a61ee5c69569bbb3db03563997e88a229eba17/deno_webgpu#deno_webgpu [eat-the-world-meaning]: https://www.quora.com/What-did-Marc-Andreessen-mean-when-he-said-that-software-is-eating-the-world -WGPU also benefits from standard, portable behavior in the same way as web browsers. Because of this behavior, it's generally fairly easy to port over usage of WebGPU in JavaScript to WGPU. It is also what lets WGPU go full circle: WGPU can be an implementation of WebGPU on native targets, but _also_ it can use _other implementations of WebGPU_ as a backend in JavaScript when compiled to WASM. Therefore, the same dynamic applies: if WGPU's own behavior were significantly different, then WGPU and end users would be _sad, sad humans_ as soon as they discover places where their nice apps are breaking, right? +wgpu also benefits from standard, portable behavior in the same way as web browsers. Because of this behavior, it's generally fairly easy to port over usage of WebGPU in JavaScript to wgpu. It is also what lets wgpu go full circle: wgpu can be an implementation of WebGPU on native targets, but _also_ it can use _other implementations of WebGPU_ as a backend in JavaScript when compiled to WASM. Therefore, the same dynamic applies: if wgpu's own behavior were significantly different, then wgpu and end users would be _sad, sad humans_ as soon as they discover places where their nice apps are breaking, right? -The answer is: yes, we _do_ have sad, sad humans that really want their WGPU code to work _everywhere_. As Firefox and others use WGPU to implement WebGPU, the above example of Firefox diverging from standard is, unfortunately, today's reality. It _mostly_ behaves the same as a standards-compliant WebGPU, but it still doesn't in many important ways. Of particular note is Naga, its implementation of the WebGPU Shader Language. Shaders are pretty much a black-and-white point of failure in GPU programming; if they don't compile, then you can't use the rest of the API! And yet, it's extremely easy to run into a case like that from : +The answer is: yes, we _do_ have sad, sad humans that really want their wgpu code to work _everywhere_. As Firefox and others use wgpu to implement WebGPU, the above example of Firefox diverging from standard is, unfortunately, today's reality. It _mostly_ behaves the same as a standards-compliant WebGPU, but it still doesn't in many important ways. Of particular note is naga, its implementation of the WebGPU Shader Language. Shaders are pretty much a black-and-white point of failure in GPU programming; if they don't compile, then you can't use the rest of the API! And yet, it's extremely easy to run into a case like that from : ```wgsl fn gimme_a_float() -> f32 { - return 42; // fails in Naga, but standard WGSL happily converts to `f32` + return 42; // fails in naga, but standard WGSL happily converts to `f32` } ``` -We intend to continue making visible strides in converging with specifications for WebGPU and WGSL, as this release has. This is, unfortunately, one of the major reasons that WGPU has no plans to work hard at keeping a SemVer-stable interface for the foreseeable future; we have an entire platform of GPU programming functionality we have to catch up with, and SemVer stability is unfortunately in tension with that. So, for now, you're going to keep seeing major releases and breaking changes. Where possible, we'll try to make that painless, but compromises to do so don't always make sense with our limited resources. +We intend to continue making visible strides in converging with specifications for WebGPU and WGSL, as this release has. This is, unfortunately, one of the major reasons that wgpu has no plans to work hard at keeping a SemVer-stable interface for the foreseeable future; we have an entire platform of GPU programming functionality we have to catch up with, and SemVer stability is unfortunately in tension with that. So, for now, you're going to keep seeing major releases and breaking changes. Where possible, we'll try to make that painless, but compromises to do so don't always make sense with our limited resources. This is also the last planned major version release of 2024; the next milestone is set for January 1st, 2025, according to our regular 12-week cadence (offset from the originally planned date of 2024-10-09 for _this_ release 😅). We'll see you next year! ### Contributor spotlight: @sagudev -This release, we'd like to spotlight the work of @sagudev, who has made significant contributions to the WGPU ecosystem this release. Among other things, they contributed a particularly notable feature where runtime-known indices are finally allowed for use with `const` array values. For example, this WGSL shader previously wasn't allowed: +This release, we'd like to spotlight the work of @sagudev, who has made significant contributions to the wgpu ecosystem this release. Among other things, they contributed a particularly notable feature where runtime-known indices are finally allowed for use with `const` array values. For example, this WGSL shader previously wasn't allowed: ```wgsl const arr: array = array(1, 2, 3, 4); @@ -972,7 +1296,7 @@ fn what_number_should_i_use(idx: u32) -> u32 { …but now it works! This is significant because this sort of shader rejection was one of the most impactful issues we are aware of for converging with the WGSL specification. There are more still to go—some of which we expect to even more drastically change how folks author shaders—but we suspect that many more will come in the next few releases, including with @sagudev's help. -We're excited for more of @sagudev's contributions via the Servo community. Oh, did we forget to mention that these contributions were motivated by their work on Servo? That's right, a _third_ well-known JavaScript runtime is now using WGPU to implement its WebGPU implementation. We're excited to support Servo to becoming another fully fledged browsing environment this way. +We're excited for more of @sagudev's contributions via the Servo community. Oh, did we forget to mention that these contributions were motivated by their work on Servo? That's right, a _third_ well-known JavaScript runtime is now using wgpu to implement its WebGPU implementation. We're excited to support Servo to becoming another fully fledged browsing environment this way. ### Major Changes @@ -1047,9 +1371,9 @@ fn fs_main() { /* … */ } [optional-entrypoint-in-spec]: https://github.com/gpuweb/gpuweb/issues/4342 -#### WGPU's DX12 backend is now based on the `windows` crate ecosystem, instead of the `d3d12` crate +#### wgpu's DX12 backend is now based on the `windows` crate ecosystem, instead of the `d3d12` crate -WGPU has retired the `d3d12` crate (based on `winapi`), and now uses the `windows` crate for interfacing with Windows. For many, this may not be a change that affects day-to-day work. However, for users who need to vet their dependencies, or who may vendor in dependencies, this may be a nontrivial migration. +wgpu has retired the `d3d12` crate (based on `winapi`), and now uses the `windows` crate for interfacing with Windows. For many, this may not be a change that affects day-to-day work. However, for users who need to vet their dependencies, or who may vendor in dependencies, this may be a nontrivial migration. By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006). @@ -1059,7 +1383,7 @@ By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006). - Added initial acceleration structure and ray query support into wgpu. By @expenses @daniel-keitel @Vecvec @JMS55 @atlv24 in [#6291](https://github.com/gfx-rs/wgpu/pull/6291) -#### Naga +#### naga - Support constant evaluation for `firstLeadingBit` and `firstTrailingBit` numeric built-ins in WGSL. Front-ends that translate to these built-ins also benefit from constant evaluation. By @ErichDonGubler in [#5101](https://github.com/gfx-rs/wgpu/pull/5101). - Add `first` and `either` sampling types for `@interpolate(flat, …)` in WGSL. By @ErichDonGubler in [#6181](https://github.com/gfx-rs/wgpu/pull/6181). @@ -1069,7 +1393,7 @@ By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006). - Support polyfilling `inverse` in WGSL. By @chyyran in [#6385](https://github.com/gfx-rs/wgpu/pull/6385). - Add base support for parsing `requires`, `enable`, and `diagnostic` directives. No extensions or diagnostic filters are yet supported, but diagnostics have improved dramatically. By @ErichDonGubler in [#6352](https://github.com/gfx-rs/wgpu/pull/6352), [#6424](https://github.com/gfx-rs/wgpu/pull/6424), [#6437](https://github.com/gfx-rs/wgpu/pull/6437). - Include error chain information as a message and notes in shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436). -- Unify Naga CLI error output with the format of shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436). +- Unify naga CLI error output with the format of shader compilation messages. By @ErichDonGubler in [#6436](https://github.com/gfx-rs/wgpu/pull/6436). #### General @@ -1089,7 +1413,7 @@ By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006). - Fix incorrect hlsl image output type conversion. By @atlv24 in [#6123](https://github.com/gfx-rs/wgpu/pull/6123). -#### Naga +#### naga - SPIR-V frontend splats depth texture sample and load results. Fixes [issue #4551](https://github.com/gfx-rs/wgpu/issues/4551). By @schell in [#6384](https://github.com/gfx-rs/wgpu/pull/6384). - Accept only `vec3` (not `vecN`) for the `cross` built-in. By @ErichDonGubler in [#6171](https://github.com/gfx-rs/wgpu/pull/6171). @@ -1113,7 +1437,7 @@ By @MarijnS95 in [#6006](https://github.com/gfx-rs/wgpu/pull/6006). - Deduplicate bind group layouts that are created from pipelines with "auto" layouts. By @teoxoy [#6049](https://github.com/gfx-rs/wgpu/pull/6049). - Document `wgpu_hal` bounds-checking promises, and adapt `wgpu_core`'s lazy initialization logic to the slightly weaker-than-expected guarantees. By @jimblandy in [#6201](https://github.com/gfx-rs/wgpu/pull/6201). - Raise validation error instead of panicking in `{Render,Compute}Pipeline::get_bind_group_layout` on native / WebGL. By @bgr360 in [#6280](https://github.com/gfx-rs/wgpu/pull/6280). -- **BREAKING**: Remove the last exposed C symbols in project, located in `wgpu_core::render::bundle::bundle_ffi`, to allow multiple versions of WGPU to compile together. By @ErichDonGubler in [#6272](https://github.com/gfx-rs/wgpu/pull/6272). +- **BREAKING**: Remove the last exposed C symbols in project, located in `wgpu_core::render::bundle::bundle_ffi`, to allow multiple versions of wgpu to compile together. By @ErichDonGubler in [#6272](https://github.com/gfx-rs/wgpu/pull/6272). - Call `flush_mapped_ranges` when unmapping write-mapped buffers. By @teoxoy in [#6089](https://github.com/gfx-rs/wgpu/pull/6089). - When mapping buffers for reading, mark buffers as initialized only when they have `MAP_WRITE` usage. By @teoxoy in [#6178](https://github.com/gfx-rs/wgpu/pull/6178). - Add a separate pipeline constants error. By @teoxoy in [#6094](https://github.com/gfx-rs/wgpu/pull/6094). @@ -1185,7 +1509,7 @@ This release includes `wgpu`, `wgpu-core` and `naga`. All other crates remain at ### Added -#### Naga +#### naga - Added back implementations of PartialEq for more IR types. By @teoxoy in [#6045](https://github.com/gfx-rs/wgpu/pull/6045) @@ -1206,21 +1530,21 @@ This release includes `wgpu`, `wgpu-core` and `naga`. All other crates remain at ### Our first major version release! -For the first time ever, WGPU is being released with a major version (i.e., 22.\* instead of 0.22.\*)! Maintainership has decided to fully adhere to [Semantic Versioning](https://semver.org/)'s recommendations for versioning production software. According to [SemVer 2.0.0's Q&A about when to use 1.0.0 versions (and beyond)](https://semver.org/spec/v2.0.0.html#how-do-i-know-when-to-release-100): +For the first time ever, wgpu is being released with a major version (i.e., 22.\* instead of 0.22.\*)! Maintainership has decided to fully adhere to [Semantic Versioning](https://semver.org/)'s recommendations for versioning production software. According to [SemVer 2.0.0's Q&A about when to use 1.0.0 versions (and beyond)](https://semver.org/spec/v2.0.0.html#how-do-i-know-when-to-release-100): > ### How do I know when to release 1.0.0? > > If your software is being used in production, it should probably already be 1.0.0. If you have a stable API on which users have come to depend, you should be 1.0.0. If you’re worrying a lot about backward compatibility, you should probably already be 1.0.0. -It is a well-known fact that WGPU has been used for applications and platforms already in production for years, at this point. We are often concerned with tracking breaking changes, and affecting these consumers' ability to ship. By releasing our first major version, we publicly acknowledge that this is the case. We encourage other projects in the Rust ecosystem to follow suit. +It is a well-known fact that wgpu has been used for applications and platforms already in production for years, at this point. We are often concerned with tracking breaking changes, and affecting these consumers' ability to ship. By releasing our first major version, we publicly acknowledge that this is the case. We encourage other projects in the Rust ecosystem to follow suit. -Note that while we start to use the major version number, WGPU is _not_ "going stable", as many Rust projects do. We anticipate many breaking changes before we fully comply with the WebGPU spec., which we expect to take a small number of years. +Note that while we start to use the major version number, wgpu is _not_ "going stable", as many Rust projects do. We anticipate many breaking changes before we fully comply with the WebGPU spec., which we expect to take a small number of years. ### Overview -A major ([pun intended](#our-first-major-version-release)) theme of this release is incremental improvement. Among the typically large set of bug fixes, new features, and other adjustments to WGPU by the many contributors listed below, @wumpf and @teoxoy have merged a series of many simplifications to WGPU's internals and, in one case, to the render and compute pass recording APIs. Many of these change WGPU to use atomically reference-counted resource tracking (i.e., `Arc<…>`), rather than using IDs to manage the lifetimes of platform-specific graphics resources in a registry of separate reference counts. This has led us to diagnose and fix many long-standing bugs, and net some neat performance improvements on the order of 40% or more of some workloads. +A major ([pun intended](#our-first-major-version-release)) theme of this release is incremental improvement. Among the typically large set of bug fixes, new features, and other adjustments to wgpu by the many contributors listed below, @wumpf and @teoxoy have merged a series of many simplifications to wgpu's internals and, in one case, to the render and compute pass recording APIs. Many of these change wgpu to use atomically reference-counted resource tracking (i.e., `Arc<…>`), rather than using IDs to manage the lifetimes of platform-specific graphics resources in a registry of separate reference counts. This has led us to diagnose and fix many long-standing bugs, and net some neat performance improvements on the order of 40% or more of some workloads. -While the above is exciting, we acknowledge already finding and fixing some (easy-to-fix) regressions from the above work. If you migrate to WGPU 22 and encounter such bugs, please engage us in the issue tracker right away! +While the above is exciting, we acknowledge already finding and fixing some (easy-to-fix) regressions from the above work. If you migrate to wgpu 22 and encounter such bugs, please engage us in the issue tracker right away! ### Major Changes @@ -1297,7 +1621,7 @@ Add the following flags to `wgpu_types::Features`: atomic operations available on Metal as of 3.1. Add corresponding flags to `naga::valid::Capabilities`. These are supported by the -WGSL front end, and all Naga backends. +WGSL front end, and all naga backends. Platform support: @@ -1342,7 +1666,7 @@ By @teoxoy in [#5901](https://github.com/gfx-rs/wgpu/pull/5901) - These hints may be ignored by some backends. Currently only the Vulkan and D3D12 backends take them into account. - Add `HTMLImageElement` and `ImageData` as external source for copying images. By @Valaphee in [#5668](https://github.com/gfx-rs/wgpu/pull/5668) -#### Naga +#### naga - Added -D, --defines option to naga CLI to define preprocessor macros by @theomonnom in [#5859](https://github.com/gfx-rs/wgpu/pull/5859) - Added type upgrades to SPIR-V atomic support. Added related infrastructure. Tracking issue is [here](https://github.com/gfx-rs/wgpu/issues/4489). By @schell in [#5775](https://github.com/gfx-rs/wgpu/pull/5775). @@ -1410,7 +1734,7 @@ By @teoxoy in [#5901](https://github.com/gfx-rs/wgpu/pull/5901) - Replace `glClear` with `glClearBufferF` because `glDrawBuffers` requires that the ith buffer must be `COLOR_ATTACHMENTi` or `NONE` [#5666](https://github.com/gfx-rs/wgpu/pull/5666) - Return the unmodified version in driver_info. By @Valaphee in [#5753](https://github.com/gfx-rs/wgpu/pull/5753) -#### Naga +#### naga - In spv-out don't decorate a `BindingArray`'s type with `Block` if the type is a struct with a runtime array by @Vecvec in [#5776](https://github.com/gfx-rs/wgpu/pull/5776) - Add `packed` as a keyword for GLSL by @kjarosh in [#5855](https://github.com/gfx-rs/wgpu/pull/5855) @@ -1454,7 +1778,7 @@ This release fixes the validation errors whenever a surface is used with the vul - Fix regression on OpenGL (EGL) where non-sRGB still used sRGB [#5642](https://github.com/gfx-rs/wgpu/pull/5642) -#### Naga +#### naga - Work around shader consumers that have bugs handling `switch` statements with a single body for all cases. These are now written as `do {} while(false);` loops in hlsl-out and glsl-out. By @Imberflur in [#5654](https://github.com/gfx-rs/wgpu/pull/5654) - In hlsl-out, defer `continue` statements in switches by setting a flag and breaking from the switch. This allows such constructs to work with FXC which does not support `continue` within a switch. By @Imberflur in [#5654](https://github.com/gfx-rs/wgpu/pull/5654) @@ -1553,9 +1877,9 @@ By @atlv24 and @cwfitzgerald in [#5154](https://github.com/gfx-rs/wgpu/pull/5154 - Added `wgpu::TextureView::as_hal` - `wgpu::Texture::as_hal` now returns a user-defined type to match the other as_hal functions -#### Naga +#### naga -- Allow user to select which MSL version to use via `--metal-version` with Naga CLI. By @pcleavelin in [#5392](https://github.com/gfx-rs/wgpu/pull/5392) +- Allow user to select which MSL version to use via `--metal-version` with naga CLI. By @pcleavelin in [#5392](https://github.com/gfx-rs/wgpu/pull/5392) - Support `arrayLength` for runtime-sized arrays inside binding arrays (for WGSL input and SPIR-V output). By @kvark in [#5428](https://github.com/gfx-rs/wgpu/pull/5428) - Added `--shader-stage` and `--input-kind` options to naga-cli for specifying vertex/fragment/compute shaders, and frontend. by @ratmice in [#5411](https://github.com/gfx-rs/wgpu/pull/5411) - Added a `create_validator` function to wgpu_core `Device` to create naga `Validator`s. By @atlv24 [#5606](https://github.com/gfx-rs/wgpu/pull/5606) @@ -1614,7 +1938,7 @@ By @atlv24 and @cwfitzgerald in [#5154](https://github.com/gfx-rs/wgpu/pull/5154 - Remove exposed C symbols (`extern "C"` + [no_mangle]) from RenderPass & ComputePass recording. By @wumpf in [#5409](https://github.com/gfx-rs/wgpu/pull/5409). - Fix surfaces being only compatible with first backend enabled on an instance, causing failures when manually specifying an adapter. By @Wumpf in [#5535](https://github.com/gfx-rs/wgpu/pull/5535). -#### Naga +#### naga - In spv-in, remove unnecessary "gl_PerVertex" name check so unused builtins will always be skipped. Prevents validation errors caused by capability requirements of these builtins [#4915](https://github.com/gfx-rs/wgpu/issues/4915). By @Imberflur in [#5227](https://github.com/gfx-rs/wgpu/pull/5227). - In spv-out, check for acceleration and ray-query types when enabling ray-query extension to prevent validation error. By @Vecvec in [#5463](https://github.com/gfx-rs/wgpu/pull/5463) @@ -1898,7 +2222,7 @@ Abstract types make numeric literals easier to use, by automatically converting literals and other constant expressions from abstract numeric types to concrete types when safe and necessary. For example, to build a vector of floating-point -numbers, Naga previously made you write: +numbers, naga previously made you write: ```rust vec3(1.0, 2.0, 3.0) @@ -1910,7 +2234,7 @@ With this change, you can now simply write: vec3(1, 2, 3) ``` -Even though the literals are abstract integers, Naga recognizes +Even though the literals are abstract integers, naga recognizes that it is safe and necessary to convert them to `f32` values in order to build the vector. You can also use abstract values as initializers for global constants and global and local variables, @@ -1921,15 +2245,15 @@ var unit_x: vec2 = vec2(1, 0); ``` The literals `1` and `0` are abstract integers, and the expression -`vec2(1, 0)` is an abstract vector. However, Naga recognizes that +`vec2(1, 0)` is an abstract vector. However, naga recognizes that it can convert that to the concrete type `vec2` to satisfy the given type of `unit_x`. The WGSL specification permits abstract integers and -floating-point values in almost all contexts, but Naga's support +floating-point values in almost all contexts, but naga's support for this is still incomplete. Many WGSL operators and builtin functions are specified to produce abstract results when applied -to abstract inputs, but for now Naga simply concretizes them all -before applying the operation. We will expand Naga's abstract type +to abstract inputs, but for now naga simply concretizes them all +before applying the operation. We will expand naga's abstract type support in subsequent pull requests. As part of this work, the public types `naga::ScalarKind` and `naga::Literal` now have new variants, `AbstractInt` and `AbstractFloat`. @@ -1981,15 +2305,15 @@ By @cwfitzgerald in [#5053](https://github.com/gfx-rs/wgpu/pull/5053) - `@builtin(instance_index)` now properly reflects the range provided in the draw call instead of always counting from 0. By @cwfitzgerald in [#4722](https://github.com/gfx-rs/wgpu/pull/4722). - Desktop GL now supports `POLYGON_MODE_LINE` and `POLYGON_MODE_POINT`. By @valaphee in [#4836](https://github.com/gfx-rs/wgpu/pull/4836). -#### Naga +#### naga -- Naga's WGSL front end now allows operators to produce values with abstract types, rather than concretizing their operands. By @jimblandy in [#4850](https://github.com/gfx-rs/wgpu/pull/4850) and [#4870](https://github.com/gfx-rs/wgpu/pull/4870). -- Naga's WGSL front and back ends now have experimental support for 64-bit floating-point literals: `1.0lf` denotes an `f64` value. There has been experimental support for an `f64` type for a while, but until now there was no syntax for writing literals with that type. As before, Naga module validation rejects `f64` values unless `naga::valid::Capabilities::FLOAT64` is requested. By @jimblandy in [#4747](https://github.com/gfx-rs/wgpu/pull/4747). -- Naga constant evaluation can now process binary operators whose operands are both vectors. By @jimblandy in [#4861](https://github.com/gfx-rs/wgpu/pull/4861). -- Add `--bulk-validate` option to Naga CLI. By @jimblandy in [#4871](https://github.com/gfx-rs/wgpu/pull/4871). -- Naga's `cargo xtask validate` now runs validation jobs in parallel, using the [jobserver](https://crates.io/crates/jobserver) protocol to limit concurrency, and offers a `validate all` subcommand, which runs all available validation types. By @jimblandy in [#4902](https://github.com/gfx-rs/wgpu/pull/4902). +- naga's WGSL front end now allows operators to produce values with abstract types, rather than concretizing their operands. By @jimblandy in [#4850](https://github.com/gfx-rs/wgpu/pull/4850) and [#4870](https://github.com/gfx-rs/wgpu/pull/4870). +- naga's WGSL front and back ends now have experimental support for 64-bit floating-point literals: `1.0lf` denotes an `f64` value. There has been experimental support for an `f64` type for a while, but until now there was no syntax for writing literals with that type. As before, naga module validation rejects `f64` values unless `naga::valid::Capabilities::FLOAT64` is requested. By @jimblandy in [#4747](https://github.com/gfx-rs/wgpu/pull/4747). +- naga constant evaluation can now process binary operators whose operands are both vectors. By @jimblandy in [#4861](https://github.com/gfx-rs/wgpu/pull/4861). +- Add `--bulk-validate` option to naga CLI. By @jimblandy in [#4871](https://github.com/gfx-rs/wgpu/pull/4871). +- naga's `cargo xtask validate` now runs validation jobs in parallel, using the [jobserver](https://crates.io/crates/jobserver) protocol to limit concurrency, and offers a `validate all` subcommand, which runs all available validation types. By @jimblandy in [#4902](https://github.com/gfx-rs/wgpu/pull/4902). - Remove `span` and `validate` features. Always fully validate shader modules, and always track source positions for use in error messages. By @teoxoy in [#4706](https://github.com/gfx-rs/wgpu/pull/4706). -- Introduce a new `Scalar` struct type for use in Naga's IR, and update all frontend, middle, and backend code appropriately. By @jimblandy in [#4673](https://github.com/gfx-rs/wgpu/pull/4673). +- Introduce a new `Scalar` struct type for use in naga's IR, and update all frontend, middle, and backend code appropriately. By @jimblandy in [#4673](https://github.com/gfx-rs/wgpu/pull/4673). - Add more metal keywords. By @fornwall in [#4707](https://github.com/gfx-rs/wgpu/pull/4707). - Add a new `naga::Literal` variant, `I64`, for signed 64-bit literals. [#4711](https://github.com/gfx-rs/wgpu/pull/4711). - Emit and init `struct` member padding always. By @ErichDonGubler in [#4701](https://github.com/gfx-rs/wgpu/pull/4701). @@ -2023,14 +2347,14 @@ By @cwfitzgerald in [#5053](https://github.com/gfx-rs/wgpu/pull/5053) - Create a hidden window per `wgpu::Instance` instead of sharing a global one. By @Zoxc in [#4603](https://github.com/gfx-rs/wgpu/issues/4603) -#### Naga +#### naga - Make module compaction preserve the module's named types, even if they are unused. By @jimblandy in [#4734](https://github.com/gfx-rs/wgpu/pull/4734). - Improve algorithm used by module compaction. By @jimblandy in [#4662](https://github.com/gfx-rs/wgpu/pull/4662). - When reading GLSL, fix the argument types of the double-precision floating-point overloads of the `dot`, `reflect`, `distance`, and `ldexp` builtin functions. Correct the WGSL generated for constructing 64-bit floating-point matrices. Add tests for all the above. By @jimblandy in [#4684](https://github.com/gfx-rs/wgpu/pull/4684). -- Allow Naga's IR types to represent matrices with elements elements of any scalar kind. This makes it possible for Naga IR types to represent WGSL abstract matrices. By @jimblandy in [#4735](https://github.com/gfx-rs/wgpu/pull/4735). +- Allow naga's IR types to represent matrices with elements elements of any scalar kind. This makes it possible for naga IR types to represent WGSL abstract matrices. By @jimblandy in [#4735](https://github.com/gfx-rs/wgpu/pull/4735). - Preserve the source spans for constants and expressions correctly across module compaction. By @jimblandy in [#4696](https://github.com/gfx-rs/wgpu/pull/4696). -- Record the names of WGSL `alias` declarations in Naga IR `Type`s. By @jimblandy in [#4733](https://github.com/gfx-rs/wgpu/pull/4733). +- Record the names of WGSL `alias` declarations in naga IR `Type`s. By @jimblandy in [#4733](https://github.com/gfx-rs/wgpu/pull/4733). #### Metal @@ -2048,7 +2372,7 @@ This release includes `naga` version 0.14.2. The crates `wgpu-core`, `wgpu-hal` ### Bug Fixes -#### Naga +#### naga - When evaluating const-expressions and generating SPIR-V, properly handle `Compose` expressions whose operands are `Splat` expressions. Such expressions are created and marked as constant by the constant evaluator. By @jimblandy in [#4695](https://github.com/gfx-rs/wgpu/pull/4695). @@ -2242,7 +2566,7 @@ By @teoxoy in [#4185](https://github.com/gfx-rs/wgpu/pull/4185) ### Added/New Features -- Re-export Naga. By @exrook in [#4172](https://github.com/gfx-rs/wgpu/pull/4172) +- Re-export naga. By @exrook in [#4172](https://github.com/gfx-rs/wgpu/pull/4172) - Add WinUI 3 SwapChainPanel support. By @ddrboxman in [#4191](https://github.com/gfx-rs/wgpu/pull/4191) ### Changes @@ -3128,7 +3452,7 @@ both `raw_window_handle::HasRawWindowHandle` and `raw_window_handle::HasRawDispl #### Vulkan - Fix `astc_hdr` formats support by @jinleili in [#2971]](https://github.com/gfx-rs/wgpu/pull/2971) -- Update to Naga b209d911 (2022-9-1) to avoid generating SPIR-V that +- Update to naga b209d911 (2022-9-1) to avoid generating SPIR-V that violates Vulkan valid usage rules `VUID-StandaloneSpirv-Flat-06202` and `VUID-StandaloneSpirv-Flat-04744`. By @jimblandy in [#3008](https://github.com/gfx-rs/wgpu/pull/3008) @@ -3156,7 +3480,7 @@ both `raw_window_handle::HasRawWindowHandle` and `raw_window_handle::HasRawDispl - Update Winit to version 0.27 and raw-window-handle to 0.5 by @wyatt-herkamp in [#2918](https://github.com/gfx-rs/wgpu/pull/2918) - Address Clippy 0.1.63 complaints. By @jimblandy in [#2977](https://github.com/gfx-rs/wgpu/pull/2977) - Don't use `PhantomData` for `IdentityManager`'s `Input` type. By @jimblandy in [#2972](https://github.com/gfx-rs/wgpu/pull/2972) -- Changed Naga variant in ShaderSource to `Cow<'static, Module>`, to allow loading global variables by @daxpedda in [#2903](https://github.com/gfx-rs/wgpu/pull/2903) +- Changed naga variant in ShaderSource to `Cow<'static, Module>`, to allow loading global variables by @daxpedda in [#2903](https://github.com/gfx-rs/wgpu/pull/2903) - Updated the maximum binding index to match the WebGPU specification by @nical in [#2957](https://github.com/gfx-rs/wgpu/pull/2957) - Add `unsafe_op_in_unsafe_fn` to Clippy lints in the entire workspace. By @ErichDonGubler in [#3044](https://github.com/gfx-rs/wgpu/pull/3044). @@ -3489,7 +3813,7 @@ DeviceDescriptor { - [WebGL] Add a downlevel capability for rendering to floating point textures by @expenses in [#2729](https://github.com/gfx-rs/wgpu/pull/2729) - allow creating wgpu::Instance from wgpu_core::Instance by @i509VCB in [#2763](https://github.com/gfx-rs/wgpu/pull/2763) - Force binding sizes to be multiples of 16 on webgl by @cwfitzgerald in [#2808](https://github.com/gfx-rs/wgpu/pull/2808) -- Add Naga variant to ShaderSource by @rttad in [#2801](https://github.com/gfx-rs/wgpu/pull/2801) +- Add naga variant to ShaderSource by @rttad in [#2801](https://github.com/gfx-rs/wgpu/pull/2801) - Implement Queue::write_buffer_with by @teoxoy in [#2777](https://github.com/gfx-rs/wgpu/pull/2777) #### Vulkan @@ -3635,7 +3959,7 @@ DeviceDescriptor { - Explicitly set Vulkan debug message types instead of !empty() by @victorvde in [#2321](https://github.com/gfx-rs/wgpu/pull/2321) - Use stencil read/write masks by @kvark in [#2382](https://github.com/gfx-rs/wgpu/pull/2382) -- Vulkan: correctly set INDEPENDENT_BLEND,make runable on Android 8.x by @jinleili in [#2498](https://github.com/gfx-rs/wgpu/pull/2498) +- Vulkan: correctly set INDEPENDENT_BLEND,make runnable on Android 8.x by @jinleili in [#2498](https://github.com/gfx-rs/wgpu/pull/2498) - Fix ASTC format mapping by @kvark in [#2476](https://github.com/gfx-rs/wgpu/pull/2476) - Support flipped Y on VK 1.1 devices by @cwfitzgerald in [#2512](https://github.com/gfx-rs/wgpu/pull/2512) - Fixed builtin(primitive_index) for vulkan backend by @kwillemsen in [#2716](https://github.com/gfx-rs/wgpu/pull/2716) @@ -3926,7 +4250,7 @@ DeviceDescriptor { - new `PARTIALLY_BOUND_BINDING_ARRAY` - `NON_FILL_POLYGON_MODE` is split into `POLYGON_MODE_LINE` and `POLYGON_MODE_POINT` - fixes: - - many shader-related fixes in Naga-0.7 + - many shader-related fixes in naga-0.7 - fix a panic in resource cleanup happening when they are dropped on another thread - Vulkan: - create SPIR-V per entry point to work around driver bugs @@ -4076,7 +4400,7 @@ DeviceDescriptor { ## v0.8 (2021-04-29) -- Naga is used by default to translate shaders, SPIRV-Cross is optional behind `cross` feature +- naga is used by default to translate shaders, SPIRV-Cross is optional behind `cross` feature - Features: - buffers are zero-initialized - downlevel limits for DX11/OpenGL support @@ -4175,7 +4499,7 @@ DeviceDescriptor { - all transfer operations - all resource creation - bind group matching to the layout - - experimental shader interface matching with Naga + - experimental shader interface matching with naga ### wgpu-core-0.5.6 (2020-07-09) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index be0bddfc1d8..04aaf6fb9c2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,18 +1,18 @@ -This document is a guide for contributions to the WGPU project. +This document is a guide for contributions to the wgpu project. ## Welcome! -First of all, welcome to the WGPU community! 👋 We're glad you want to -contribute. If you are unfamiliar with the WGPU project, we recommend you read +First of all, welcome to the wgpu community! 👋 We're glad you want to +contribute. If you are unfamiliar with the wgpu project, we recommend you read [`GOVERNANCE.md`] for an overview of its goals, and how it's governed. ## Documentation Overview: -- [`GOVERNANCE.md`]: An overview of the WGPU project's goals and governance. -- [`CODE_OF_CONDUCT.md`]: The code of conduct for the WGPU project. -- [`docs/release-checklist.md`]: Checklist for creating a new release of WGPU. -- [`docs/review-checklist.md`]: Checklist for reviewing a pull request in WGPU. -- [`docs/testing.md`]: Information on the test suites in WGPU and Naga. +- [`GOVERNANCE.md`]: An overview of the wgpu project's goals and governance. +- [`CODE_OF_CONDUCT.md`]: The code of conduct for the wgpu project. +- [`docs/release-checklist.md`]: Checklist for creating a new release of wgpu. +- [`docs/review-checklist.md`]: Checklist for reviewing a pull request in wgpu. +- [`docs/testing.md`]: Information on the test suites in wgpu and naga. [`GOVERNANCE.md`]: ./GOVERNANCE.md [`CODE_OF_CONDUCT.md`]: ./CODE_OF_CONDUCT.md @@ -20,9 +20,9 @@ contribute. If you are unfamiliar with the WGPU project, we recommend you read [`docs/review-checklist.md`]: ./docs/review-checklist.md [`docs/testing.md`]: ./docs/testing.md -## Talking to other humans in the WGPU project +## Talking to other humans in the wgpu project -The WGPU project has multiple official platforms for community engagement: +The wgpu project has multiple official platforms for community engagement: - The Matrix channel [`wgpu:matrix.org`](https://matrix.to/#/#wgpu:matrix.org) is dedicated to informal chat about contributions the project. It is @@ -73,10 +73,10 @@ The WGPU project has multiple official platforms for community engagement: [Meeting Link]: https://meet.google.com/ubo-ztcw-gwf [`CODE_OF_CONDUCT.md`]: ./CODE_OF_CONDUCT.md -## Contributing to WGPU +## Contributing to wgpu Community response to contributions are, in general, prioritized based on their -relevance to WGPU's mission and decision-making groups' interest (see +relevance to wgpu's mission and decision-making groups' interest (see [`GOVERNANCE.md`]). ### "What can I work on?" as a new contributor @@ -84,24 +84,24 @@ relevance to WGPU's mission and decision-making groups' interest (see TODO We discourage new contributors from submitting large changes or opinionated -refactors unless they have been specifically validated by WGPU maintainership. +refactors unless they have been specifically validated by wgpu maintainership. These are likely to be rejected on basis of needing discussion before a formal review. -### Setting up a WGPU development environment +### Setting up a wgpu development environment -We use the following components in a WGPU development environment: +We use the following components in a wgpu development environment: - [A Rust toolchain][install-rust] matching the version specified in - [`rust-toolchain.toml`](./rust-toolchain.toml), to compile WGPU's code. If you + [`rust-toolchain.toml`](./rust-toolchain.toml), to compile wgpu's code. If you use `rustup`, this will be automatically installed when you first run a `cargo` command in the repository. - [Taplo](https://taplo.tamasfe.dev/) to keep TOML files formatted. - [Vulkan SDK](https://vulkan.lunarg.com/) to provide Vulkan validation layers and other Vulkan/SPIR-V tools for testing. -Once these are done, you should be ready to hack on WGPU! Drop into your -favorite editor, make some changes to the repository's code, and test that WGPU +Once these are done, you should be ready to hack on wgpu! Drop into your +favorite editor, make some changes to the repository's code, and test that wgpu has been changed the way you expect. Take a look at [`docs/testing.md`] for more info on testing. @@ -111,8 +111,8 @@ and a [`git` dependency][git-deps] pointing to your own fork to share changes with other contributors. Once you are ready to request a review of your changes so they become part of -WGPU public history, create a pull request with your changes committed to a -branch in your own fork of WGPU in GitHub. See documentation for that +wgpu public history, create a pull request with your changes committed to a +branch in your own fork of wgpu in GitHub. See documentation for that [here](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request-from-a-fork). [install-rust]: https://www.rust-lang.org/tools/install @@ -157,9 +157,9 @@ assignment is simply to ensure that pull requests don't get neglected. #### Designing new features -As an open source project, WGPU wants to serve a broad audience. This +As an open source project, wgpu wants to serve a broad audience. This helps us cast a wide net for contributors, and widens the impact of -their work. However, WGPU does not promise to incorporate every +their work. However, wgpu does not promise to incorporate every proposed feature. Large efforts that are ultimately rejected tend to burn contributors @@ -176,11 +176,11 @@ Contributors should anticipate that the larger and more complex a pull request is, the less likely it is that reviewers will accept it, regardless of its merits. -The WGPU project has had poor experiences with large, complex pull +The wgpu project has had poor experiences with large, complex pull requests: - Complex pull requests are difficult to review effectively. It is - common for us to debug a problem in WGPU and find that it was + common for us to debug a problem in wgpu and find that it was introduced by some massive pull request that we had reviewed and accepted, showing that we obviously hadn't understood it as well as we'd thought. @@ -190,7 +190,7 @@ requests: stressful to question its design decisions, knowing that changing them will require the author to essentially reimplement the project from scratch. Such pull requests make it hard for maintainers to - uphold their responsibility to keep WGPU maintainable. Incremental + uphold their responsibility to keep wgpu maintainable. Incremental changes are easier to discuss and revise without drama. These problems are serious enough that maintainers may choose to @@ -199,7 +199,7 @@ feature or the technical merit of the code. The problem isn't really the *size* of the pull request: a simple rename, with no changes to functionality, might touch hundreds of -files, but be easy to review. Or, a change to Naga might affect dozens +files, but be easy to review. Or, a change to naga might affect dozens of snapshot test output files, without being hard to understand. Rather, the problem is the *complexity* of the pull request: how many diff --git a/Cargo.lock b/Cargo.lock index 7681964afa8..f3a4f10c20e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "ab_glyph" -version = "0.2.30" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e0f4f6fbdc5ee39f2ede9f5f3ec79477271a6d6a2baff22310d51736bda6cea" +checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" dependencies = [ "ab_glyph_rasterizer", "owned_ttf_parser", @@ -14,25 +14,19 @@ dependencies = [ [[package]] name = "ab_glyph_rasterizer" -version = "0.1.9" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2187590a23ab1e3df8681afdf0987c48504d80291f002fcdb651f0ef5e25169" +checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" [[package]] name = "addr2line" -version = "0.24.2" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - [[package]] name = "adler2" version = "2.0.1" @@ -61,12 +55,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android-activity" version = "0.5.2" @@ -74,7 +62,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289" dependencies = [ "android-properties", - "bitflags 2.9.1", + "bitflags 2.9.4", "cc", "cesu8", "jni", @@ -95,7 +83,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" dependencies = [ "android-properties", - "bitflags 2.9.1", + "bitflags 2.9.4", "cc", "cesu8", "jni", @@ -132,9 +120,9 @@ checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstream" -version = "0.6.19" +version = "0.6.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933" +checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192" dependencies = [ "anstyle", "anstyle-parse", @@ -147,9 +135,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.11" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "862ed96ca487e809f1c8e5a8447f6ee2cf102f846893800b20cebdf541fc6bbd" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -162,29 +150,29 @@ dependencies = [ [[package]] name = "anstyle-query" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9" +checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2" dependencies = [ - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anstyle-wincon" -version = "3.0.9" +version = "3.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882" +checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a" dependencies = [ "anstyle", "once_cell_polyfill", - "windows-sys 0.59.0", + "windows-sys 0.60.2", ] [[package]] name = "anyhow" -version = "1.0.98" +version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" [[package]] name = "approx" @@ -197,9 +185,9 @@ dependencies = [ [[package]] name = "arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" dependencies = [ "derive_arbitrary", ] @@ -268,9 +256,9 @@ dependencies = [ [[package]] name = "async-trait" -version = "0.1.88" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", @@ -297,17 +285,17 @@ checksum = "7b7e4c2464d97fe331d41de9d5db0def0a96f4d823b8b32a2efd503578988973" [[package]] name = "backtrace" -version = "0.3.75" +version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", "libc", - "miniz_oxide 0.8.9", + "miniz_oxide", "object", "rustc-demangle", - "windows-targets 0.52.6", + "windows-link 0.2.0", ] [[package]] @@ -316,22 +304,13 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "base64-simd" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "781dd20c3aff0bd194fe7d2a977dd92f21c173891f3a03b677359e5fa457e5d5" -dependencies = [ - "simd-abstraction", -] - [[package]] name = "base64-simd" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" dependencies = [ - "outref 0.5.2", + "outref", "vsimd", ] @@ -344,13 +323,33 @@ dependencies = [ "serde", ] +[[package]] +name = "bincode" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36eaf5d7b090263e8150820482d5d93cd964a81e4019913c972f4edcc6edb740" +dependencies = [ + "bincode_derive", + "serde", + "unty", +] + +[[package]] +name = "bincode_derive" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf95709a440f45e986983918d0e8a1f30a9b1df04918fc828670606804ac3c09" +dependencies = [ + "virtue", +] + [[package]] name = "bindgen" -version = "0.70.1" +version = "0.71.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" +checksum = "5f58bf3d7db68cfbac37cfc485a8d711e87e064c3d0fe0435b92f7a407f9d6b3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cexpr", "clang-sys", "itertools 0.13.0", @@ -359,35 +358,20 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash", + "rustc-hash 2.1.1", "shlex", "syn", ] -[[package]] -name = "bit-set" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" -dependencies = [ - "bit-vec 0.6.3", -] - [[package]] name = "bit-set" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ - "bit-vec 0.8.0", + "bit-vec", ] -[[package]] -name = "bit-vec" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" - [[package]] name = "bit-vec" version = "0.8.0" @@ -402,9 +386,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.9.1" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" dependencies = [ "arbitrary", "serde", @@ -456,6 +440,16 @@ dependencies = [ "objc2 0.5.2", ] +[[package]] +name = "boxed_error" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17d4f95e880cfd28c4ca5a006cf7f6af52b4bcb7b5866f573b2faa126fb7affb" +dependencies = [ + "quote", + "syn", +] + [[package]] name = "bumpalo" version = "3.19.0" @@ -464,18 +458,18 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "bytemuck" -version = "1.23.1" +version = "1.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422" +checksum = "3995eaeebcdf32f91f980d360f78732ddc061097ab4e39991ae7a6ace9194677" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.9.3" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ecc273b49b3205b83d648f0690daa588925572cc5063745bfe547fe7ec8e1a1" +checksum = "4f154e572231cb6ba2bd1176980827e3d5dc04cc183a75dea38109fbdd672d29" dependencies = [ "proc-macro2", "quote", @@ -500,7 +494,7 @@ version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "log", "polling", "rustix 0.38.44", @@ -514,7 +508,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "log", "polling", "rustix 0.38.44", @@ -548,20 +542,11 @@ dependencies = [ [[package]] name = "camino" -version = "1.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0da45bc31171d8d6960122e222a67740df867c1dd53b4d51caa297084c185cab" -dependencies = [ - "serde", -] - -[[package]] -name = "capacity_builder" -version = "0.1.3" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58ec49028cb308564429cd8fac4ef21290067a0afe8f5955330a8d487d0d790c" +checksum = "276a59bf2b2c967788139340c9f0c5b12d7fd6630315c15c217e559de85d2609" dependencies = [ - "itoa", + "serde_core", ] [[package]] @@ -586,42 +571,25 @@ dependencies = [ [[package]] name = "cargo-platform" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84982c6c0ae343635a3a4ee6dedef965513735c8b183caa7289fa6e27399ebd4" -dependencies = [ - "serde", -] - -[[package]] -name = "cargo-util-schemas" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63d2780ac94487eb9f1fea7b0d56300abc9eb488800854ca217f102f5caccca" +checksum = "122ec45a44b270afd1402f351b782c676b173e3c3fb28d86ff7ebfb4d86a4ee4" dependencies = [ - "semver 1.0.26", "serde", - "serde-untagged", - "serde-value", - "thiserror 1.0.69", - "toml", - "unicode-xid", - "url", ] [[package]] name = "cargo_metadata" -version = "0.20.0" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502" +checksum = "981a6f317983eec002839b90fae7411a85621410ae591a9cab2ecf5cb5744873" dependencies = [ "camino", "cargo-platform", - "cargo-util-schemas", - "semver 1.0.26", + "semver", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] @@ -632,10 +600,11 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.29" +version = "1.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362" +checksum = "e1354349954c6fc9cb0deab020f27f783cf0b604e8bb754dc4658ecf0d29c35f" dependencies = [ + "find-msvc-tools", "jobserver", "libc", "shlex", @@ -658,9 +627,9 @@ dependencies = [ [[package]] name = "cfg-if" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268" +checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cfg_aliases" @@ -723,9 +692,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.40" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f" +checksum = "e2134bb3ea021b78629caa971416385309e0131b351b25e01dc16fb54e1b5fae" dependencies = [ "clap_builder", "clap_derive", @@ -733,9 +702,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.40" +version = "4.5.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e" +checksum = "c2ba64afa3c0a6df7fa517765e31314e983f51dda798ffba27b988194fb65dc9" dependencies = [ "anstream", "anstyle", @@ -745,11 +714,11 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.40" +version = "4.5.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce" +checksum = "bbfd7eae0b0f1a6e63d4b13c9c478de77c2eb546fba158ad50b4203dc24b9f9c" dependencies = [ - "heck 0.5.0", + "heck", "proc-macro2", "quote", "syn", @@ -799,15 +768,15 @@ dependencies = [ [[package]] name = "console" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e09ced7ebbccb63b4c65413d821f2e00ce54c5ca4514ddc6b3c892fdbcbc69d" +checksum = "b430743a6eb14e9764d4260d4c0d8123087d504eeb9c48f2b2a5e810dd369df4" dependencies = [ "encode_unicode", "libc", "once_cell", "unicode-width", - "windows-sys 0.60.2", + "windows-sys 0.61.1", ] [[package]] @@ -832,9 +801,12 @@ dependencies = [ [[package]] name = "const_panic" -version = "0.2.12" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" +checksum = "e262cdaac42494e3ae34c43969f9cdeb7da178bdb4b66fa6a1ea2edb4c8ae652" +dependencies = [ + "typewit", +] [[package]] name = "cooked-waker" @@ -898,25 +870,25 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "core-foundation 0.10.1", "libc", ] [[package]] name = "crc32fast" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" dependencies = [ "cfg-if", ] [[package]] name = "criterion" -version = "0.6.0" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bf7af66b0989381bd0be551bd7cc91912a655a58c6918420c9527b1fd8b4679" +checksum = "e1c047a62b0cc3e145fa84415a3191f628e980b194c2755aa12300a4e6cbd928" dependencies = [ "anes", "cast", @@ -937,12 +909,12 @@ dependencies = [ [[package]] name = "criterion-plot" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +checksum = "9b1bcc0dc7dfae599d84ad0b1a55f80cde8af3725da8313b528da95ef783e338" dependencies = [ "cast", - "itertools 0.10.5", + "itertools 0.13.0", ] [[package]] @@ -978,9 +950,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "ctor" -version = "0.4.2" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4735f265ba6a1188052ca32d461028a7d1125868be18e287e756019da7607b5" +checksum = "67773048316103656a637612c4a62477603b777d91d9c62ff2290f9cde178fdb" dependencies = [ "ctor-proc-macro", "dtor", @@ -988,16 +960,17 @@ dependencies = [ [[package]] name = "ctor-proc-macro" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f211af61d8efdd104f96e57adf5e426ba1bc3ed7a4ead616e15e5881fd79c4d" +checksum = "e2931af7e13dc045d8e9d26afccc6fa115d64e115c9c84b1166288b46f6782c2" [[package]] name = "cts_runner" -version = "26.0.0" +version = "27.0.4" dependencies = [ "deno_console", "deno_core", + "deno_features", "deno_url", "deno_web", "deno_webgpu", @@ -1031,26 +1004,27 @@ dependencies = [ [[package]] name = "deno_console" -version = "0.192.0" +version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca40d9ecd49a0320c058eff8ad8b53c83b1b743e3087001afe2e14ec50197d34" +checksum = "74189b98d1245bcef3a7565cbfe5cad4ab3d2ad9393aa48067fab89efe85a99b" dependencies = [ "deno_core", ] [[package]] name = "deno_core" -version = "0.338.0" +version = "0.355.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "113f3f08bd5daf99f1a7876c0f99cd8c3c609439fa0b808311ec856a253e95f0" +checksum = "775d2fde80a2ec3116d179703b38346a931bb9626f4a826148d5fe8631cab29f" dependencies = [ "anyhow", "az", - "bincode", - "bit-set 0.5.3", - "bit-vec 0.6.3", + "bincode 1.3.3", + "bit-set", + "bit-vec", + "boxed_error", "bytes", - "capacity_builder 0.1.3", + "capacity_builder", "cooked-waker", "deno_core_icudata", "deno_error", @@ -1060,7 +1034,6 @@ dependencies = [ "futures", "indexmap", "libc", - "memoffset", "parking_lot", "percent-encoding", "pin-project", @@ -1070,7 +1043,7 @@ dependencies = [ "smallvec", "sourcemap", "static_assertions", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "url", "v8", @@ -1085,9 +1058,9 @@ checksum = "fe4dccb6147bb3f3ba0c7a48e993bfeb999d2c2e47a81badee80e2b370c8d695" [[package]] name = "deno_error" -version = "0.5.5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c23dbc46d5804814b08b4675838f9884e3a52916987ec5105af36d42f9911b5" +checksum = "dde60bd153886964234c5012d3d9caf788287f28d81fb24a884436904101ef10" dependencies = [ "deno_error_macro", "libc", @@ -1099,65 +1072,83 @@ dependencies = [ [[package]] name = "deno_error_macro" -version = "0.5.5" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "babccedee31ce7e57c3e6dff2cb3ab8d68c49d0df8222fe0d11d628e65192790" +checksum = "409f265785bd946d3006756955aaf40b0e4deb25752eae6a990afe54a31cfd83" dependencies = [ "proc-macro2", "quote", "syn", ] +[[package]] +name = "deno_features" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea79a01229bd5f8ee4ba784811f421a90a1ac241da4dc015a3f58380167c9944" +dependencies = [ + "deno_core", + "serde", + "serde_json", +] + [[package]] name = "deno_ops" -version = "0.214.0" +version = "0.231.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ad885bf882be535f7714c713042129acba6f31a8efb5e6b2298f6e40cab9b16" +checksum = "9ca530772bbcbc9ad389ad7bcd86623b2ec555f68a2d062d23cc008915cbe781" dependencies = [ "indexmap", "proc-macro-rules", "proc-macro2", "quote", "stringcase", - "strum 0.25.0", - "strum_macros 0.25.3", + "strum", + "strum_macros", "syn", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "deno_path_util" -version = "0.3.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c238a664a0a6f1ce0ff2b73c6854811526d00f442a12f878cb8555b23fe13aa3" +checksum = "bfe02936964b2910719bd488841f6e884349360113c7abf6f4c6b28ca9cd7a19" dependencies = [ "deno_error", "percent-encoding", "sys_traits", - "thiserror 2.0.12", + "thiserror 2.0.17", "url", ] [[package]] name = "deno_permissions" -version = "0.52.0" +version = "0.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1ff15740ddc4626cc7f5d66a113e0f825073476d1e83af9fa693426516d07b7" +checksum = "656d8cc364ca906e12fb83d379202983052c6aa3e7420e385559e903d2773f61" dependencies = [ - "capacity_builder 0.5.0", - "deno_core", + "capacity_builder", "deno_error", "deno_path_util", "deno_terminal", + "deno_unsync", "fqdn", + "ipnetwork", "libc", "log", + "nix", "once_cell", + "parking_lot", "percent-encoding", "serde", - "thiserror 2.0.12", - "which 6.0.3", + "serde_json", + "sys_traits", + "thiserror 2.0.17", + "url", + "which 8.0.0", "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -1183,24 +1174,23 @@ dependencies = [ [[package]] name = "deno_url" -version = "0.192.0" +version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36a705094c7fbbb01338e89c12367da939cf831dd5b202e3a521f75a614a6082" +checksum = "a4f02fd80dda41f1e278c69170b802004c0094ceef2dde68bc117a69dc5702cf" dependencies = [ "deno_core", "deno_error", - "thiserror 2.0.12", "urlpattern", ] [[package]] name = "deno_web" -version = "0.224.0" +version = "0.245.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae1ba442b2c4b6116eb75e6e46968840693f2036d035bd45421c4a9beda6186" +checksum = "56fbbfb92fa76a4e0873d1b02f5e9947562e70ac7ec065ba794b08aca3760564" dependencies = [ "async-trait", - "base64-simd 0.8.0", + "base64-simd", "bytes", "deno_core", "deno_error", @@ -1209,14 +1199,14 @@ dependencies = [ "flate2", "futures", "serde", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "uuid", ] [[package]] name = "deno_webgpu" -version = "0.157.0" +version = "0.181.0" dependencies = [ "deno_core", "deno_error", @@ -1225,7 +1215,7 @@ dependencies = [ "raw-window-handle 0.6.2", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "tokio", "wgpu-core", "wgpu-types", @@ -1233,18 +1223,18 @@ dependencies = [ [[package]] name = "deno_webidl" -version = "0.192.0" +version = "0.214.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30d2d820d865651e0ce4eca898176577c8693bf2694af32545ab4b9ffb9934fa" +checksum = "b29a947a96dc0f672741669470f0260eba20d198436143b40afb9bda97723f07" dependencies = [ "deno_core", ] [[package]] name = "derive_arbitrary" -version = "1.4.1" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", @@ -1306,18 +1296,18 @@ checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] name = "dtor" -version = "0.0.6" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97cbdf2ad6846025e8e25df05171abfb30e3ababa12ee0a0e44b9bbe570633a8" +checksum = "e58a0764cddb55ab28955347b45be00ade43d4d6f3ba4bf3dc354e4ec9432934" dependencies = [ "dtor-proc-macro", ] [[package]] name = "dtor-proc-macro" -version = "0.0.5" +version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7454e41ff9012c00d53cf7f475c5e3afa3b91b7c90568495495e8d9bf47a1055" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" [[package]] name = "either" @@ -1327,30 +1317,29 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "encase" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6257b506d94265d4ec55a02fc6fe8a7311c1718e99cd5d2fc63c76674a94c801" +checksum = "6e3e0ff2ee0b7aa97428308dd9e1e42369cb22f5fb8dc1c55546637443a60f1e" dependencies = [ "const_panic", "encase_derive", - "glam", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "encase_derive" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71a9aa6b6a1caf2d2e04e4adcee9c8466e86cd84129bd8e9d15bad2b003396af" +checksum = "a4d90c5d7d527c6cb8a3b114efd26a6304d9ab772656e73d8f4e32b1f3d601a2" dependencies = [ "encase_derive_impl", ] [[package]] name = "encase_derive_impl" -version = "0.11.1" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a79c61182bf68a5dff807c3feb93d5348310afdb3fbe4c907db0e1696f500f" +checksum = "c8bad72d8308f7a382de2391ec978ddd736e0103846b965d7e2a63a75768af30" dependencies = [ "proc-macro2", "quote", @@ -1365,9 +1354,9 @@ checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" -version = "0.8.33" +version = "0.8.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7268b386296a025e474d5140678f75d6de9493ae55a5d709eeb9dd08149945e1" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" dependencies = [ "cfg-if", ] @@ -1407,24 +1396,14 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" -[[package]] -name = "erased-serde" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e004d887f51fcb9fef17317a2f3525c887d8aa3f4f50fed920816a688284a5b7" -dependencies = [ - "serde", - "typeid", -] - [[package]] name = "errno" -version = "0.3.13" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.59.0", ] [[package]] @@ -1457,6 +1436,12 @@ dependencies = [ "log", ] +[[package]] +name = "find-msvc-tools" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ced73b1dacfc750a6db6c0a0c3a3853c8b41997e2e2c563dc90804ae6867959" + [[package]] name = "fixedbitset" version = "0.4.2" @@ -1476,7 +1461,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a3d7db9596fecd151c5f638c0ee5d5bd487b6e0ea232e5dc96d5250f6f94b1d" dependencies = [ "crc32fast", - "miniz_oxide 0.8.9", + "miniz_oxide", ] [[package]] @@ -1497,6 +1482,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "foreign-types" version = "0.5.0" @@ -1526,9 +1517,9 @@ checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" [[package]] name = "form_urlencoded" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] @@ -1605,9 +1596,9 @@ checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" -version = "2.6.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ "fastrand", "futures-core", @@ -1659,9 +1650,9 @@ dependencies = [ [[package]] name = "generator" -version = "0.8.5" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d18470a76cb7f8ff746cf1f7470914f900252ec36bbc40b569d74b1258446827" +checksum = "605183a538e3e2a9c1038635cc5c2d194e2ee8fd0d1b66b8349fad7dbacce5a2" dependencies = [ "cc", "cfg-if", @@ -1673,12 +1664,12 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "fc257fdb4038301ce4b9cd1b3b51704509692bb3ff716a410cbd07925d9dae55" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.2", + "windows-targets 0.52.6", ] [[package]] @@ -1701,16 +1692,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", - "wasi 0.14.2+wasi-0.2.4", + "wasi 0.14.7+wasi-0.2.4", + "wasm-bindgen", ] [[package]] name = "gimli" -version = "0.31.1" +version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "gl_generator" @@ -1725,18 +1718,19 @@ dependencies = [ [[package]] name = "glam" -version = "0.30.4" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50a99dbe56b72736564cfa4b85bf9a33079f16ae8b74983ab06af3b1a3696b11" +checksum = "e12d847aeb25f41be4c0ec9587d624e9cd631bc007a8fd7ce3f5851e064c6460" dependencies = [ "bytemuck", + "encase", ] [[package]] name = "glob" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" [[package]] name = "glow" @@ -1756,7 +1750,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg_aliases 0.1.1", "cgl", "core-foundation 0.9.4", @@ -1819,7 +1813,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "gpu-alloc-types", ] @@ -1829,7 +1823,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] @@ -1850,9 +1844,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "gpu-descriptor-types", - "hashbrown", + "hashbrown 0.15.5", ] [[package]] @@ -1861,7 +1855,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] @@ -1889,21 +1883,22 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.15.4" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "allocator-api2", - "equivalent", - "foldhash", - "serde", + "foldhash 0.1.5", ] [[package]] -name = "heck" -version = "0.4.1" +name = "hashbrown" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +dependencies = [ + "foldhash 0.2.0", + "serde", +] [[package]] name = "heck" @@ -1925,7 +1920,7 @@ checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" [[package]] name = "hlsl-snapshots" -version = "26.0.0" +version = "27.0.4" dependencies = [ "anyhow", "nanoserde", @@ -2039,9 +2034,9 @@ dependencies = [ [[package]] name = "idna" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", @@ -2060,32 +2055,34 @@ dependencies = [ [[package]] name = "if_chain" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" +checksum = "cd62e6b5e86ea8eeeb8db1de02880a6abc01a397b2ebb64b5d74ac255318f5cb" [[package]] name = "image" -version = "0.25.6" +version = "0.25.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" +checksum = "529feb3e6769d234375c4cf1ee2ce713682b8e76538cb13f9fc23e1400a591e7" dependencies = [ "bytemuck", "byteorder-lite", + "moxcms", "num-traits", "png", ] [[package]] name = "indexmap" -version = "2.10.0" +version = "2.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe4cd85333e22411419a0bcae1297d25e58c9443848b11dc6a86fefe8c78a661" +checksum = "4b0f83760fb341a774ed326568e19f5a863af4a952def8c39f9ab92fd95b88e5" dependencies = [ "arbitrary", "equivalent", - "hashbrown", + "hashbrown 0.16.0", "serde", + "serde_core", ] [[package]] @@ -2103,29 +2100,29 @@ dependencies = [ [[package]] name = "io-uring" -version = "0.7.8" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013" +checksum = "046fa2d4d00aea763528b4950358d0ead425372445dc8ff86312b3c69ff7727b" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cfg-if", "libc", ] [[package]] -name = "is_terminal_polyfill" -version = "1.70.1" +name = "ipnetwork" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" +checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" +dependencies = [ + "serde", +] [[package]] -name = "itertools" -version = "0.10.5" +name = "is_terminal_polyfill" +version = "1.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" -dependencies = [ - "either", -] +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" [[package]] name = "itertools" @@ -2199,9 +2196,9 @@ checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] name = "jobserver" -version = "0.1.33" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ "getrandom 0.3.3", "libc", @@ -2209,9 +2206,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +checksum = "ec48937a97411dcb524a265206ccd4c90bb711fca92b2792c407f268825b9305" dependencies = [ "once_cell", "wasm-bindgen", @@ -2240,7 +2237,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff7f53bdf698e7aa7ec916411bbdc8078135da11b66db5182675b2227f6c0d07" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] @@ -2251,9 +2248,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.174" +version = "0.2.176" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776" +checksum = "58f929b4d672ea937a23a1ab494143d968337a5f47e56d0815df1e0890ddf174" [[package]] name = "libfuzzer-sys" @@ -2268,12 +2265,12 @@ dependencies = [ [[package]] name = "libloading" -version = "0.8.8" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07033963ba89ebaf1584d767badaa2e8fcec21aedea6b8c0346d487d49c28667" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" dependencies = [ "cfg-if", - "windows-targets 0.53.2", + "windows-link 0.2.0", ] [[package]] @@ -2284,13 +2281,13 @@ checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" -version = "0.1.4" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1580801010e535496706ba011c15f8532df6b42297d2e471fec38ceadd8c0638" +checksum = "416f7e718bdb06000964960ffa43b4335ad4012ae8b99060261aa4a8088d5ccb" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "libc", - "redox_syscall 0.5.13", + "redox_syscall 0.5.17", ] [[package]] @@ -2313,9 +2310,9 @@ checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" [[package]] name = "litemap" @@ -2325,13 +2322,13 @@ checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "litrs" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" +checksum = "f5e54036fe321fd421e10d732f155734c4e4afd610dd556d9a82833ab3ee0bed" [[package]] name = "lock-analyzer" -version = "26.0.0" +version = "27.0.4" dependencies = [ "anyhow", "ron", @@ -2350,9 +2347,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.27" +version = "0.4.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" [[package]] name = "loom" @@ -2384,44 +2381,35 @@ dependencies = [ [[package]] name = "matchers" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8263075bb86c5a1b1427b5ae862e8889656f126e9f77c484496e8b47cf5c5558" +checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" dependencies = [ - "regex-automata 0.1.10", + "regex-automata", ] [[package]] name = "memchr" -version = "2.7.5" +version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" [[package]] name = "memmap2" -version = "0.9.5" +version = "0.9.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +checksum = "843a98750cd611cc2965a8213b53b43e715f13c37a9e096c6408e69990961db7" dependencies = [ "libc", ] -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - [[package]] name = "metal" version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block", "core-graphics-types 0.2.0", "foreign-types", @@ -2446,15 +2434,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "miniz_oxide" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" -dependencies = [ - "adler", -] - [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2476,50 +2455,60 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "moxcms" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd32fa8935aeadb8a8a6b6b351e40225570a37c43de67690383d87ef170cd08" +dependencies = [ + "num-traits", + "pxfm", +] + [[package]] name = "naga" -version = "26.0.0" +version = "27.0.4" dependencies = [ "arbitrary", "arrayvec", - "bit-set 0.8.0", - "bitflags 2.9.1", + "bit-set", + "bitflags 2.9.4", "cfg-if", "cfg_aliases 0.2.1", "codespan-reporting", "diff", "env_logger", "half", - "hashbrown", + "hashbrown 0.16.0", "hexf-parse", "hlsl-snapshots", "indexmap", "itertools 0.14.0", "libm", "log", + "naga-test", "num-traits", "once_cell", - "petgraph 0.8.2", + "petgraph 0.8.3", "pp-rs", "ron", "rspirv", - "rustc-hash", + "rustc-hash 1.1.0", "serde", - "spirv 0.3.0+sdk-1.3.268.0", - "strum 0.27.1", - "thiserror 2.0.12", - "toml", + "spirv", + "strum", + "thiserror 2.0.17", "unicode-ident", "walkdir", ] [[package]] name = "naga-cli" -version = "26.0.0" +version = "27.0.4" dependencies = [ "anyhow", "argh", - "bincode", + "bincode 2.0.1", "codespan-reporting", "env_logger", "log", @@ -2528,7 +2517,7 @@ dependencies = [ [[package]] name = "naga-fuzz" -version = "26.0.0" +version = "27.0.4" dependencies = [ "arbitrary", "cfg_aliases 0.2.1", @@ -2536,6 +2525,22 @@ dependencies = [ "naga", ] +[[package]] +name = "naga-test" +version = "27.0.4" +dependencies = [ + "bitflags 2.9.4", + "env_logger", + "naga", + "ron", + "rspirv", + "serde", + "serde_json", + "spirv", + "toml", + "walkdir", +] + [[package]] name = "naga-xtask" version = "0.1.0" @@ -2567,6 +2572,9 @@ name = "nanorand" version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e3d189da485332e96ba8a5ef646a311871abd7915bf06ac848a9117f19cf6e4" +dependencies = [ + "getrandom 0.3.3", +] [[package]] name = "nanoserde" @@ -2589,7 +2597,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "jni-sys", "log", "ndk-sys 0.5.0+25.2.9519653", @@ -2605,7 +2613,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "jni-sys", "log", "ndk-sys 0.6.0+11769913", @@ -2638,6 +2646,17 @@ dependencies = [ "jni-sys", ] +[[package]] +name = "nix" +version = "0.27.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" +dependencies = [ + "bitflags 2.9.4", + "cfg-if", + "libc", +] + [[package]] name = "noise" version = "0.9.0" @@ -2661,12 +2680,11 @@ dependencies = [ [[package]] name = "nu-ansi-term" -version = "0.46.0" +version = "0.50.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77a8165726e8236064dbb45459242600304b42a5ea24ee2948e18e023bf7ba84" +checksum = "d4a28e057d01f97e61255210fcff094d74ed0466038633e95017f5beb68e4399" dependencies = [ - "overload", - "winapi", + "windows-sys 0.52.0", ] [[package]] @@ -2796,7 +2814,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -2812,7 +2830,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -2836,7 +2854,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation", @@ -2884,7 +2902,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "dispatch", "libc", @@ -2909,7 +2927,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation", @@ -2921,7 +2939,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation", @@ -2944,7 +2962,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit", @@ -2976,7 +2994,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -2985,9 +3003,9 @@ dependencies = [ [[package]] name = "object" -version = "0.36.7" +version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] @@ -3021,45 +3039,24 @@ dependencies = [ [[package]] name = "ordered-float" -version = "2.10.1" +version = "5.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-float" -version = "5.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2c1f9f56e534ac6a9b8a4600bdf0f530fb393b5f393e7b4d03489c3cf0c3f01" +checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" dependencies = [ "num-traits", ] -[[package]] -name = "outref" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f222829ae9293e33a9f5e9f440c6760a3d450a64affe1846486b140db81c1f4" - [[package]] name = "outref" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" -[[package]] -name = "overload" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" - [[package]] name = "owned_ttf_parser" -version = "0.25.0" +version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ "ttf-parser", ] @@ -3090,7 +3087,7 @@ dependencies = [ "cfg-if", "libc", "petgraph 0.6.5", - "redox_syscall 0.5.13", + "redox_syscall 0.5.17", "smallvec", "thread-id", "windows-targets 0.52.6", @@ -3104,9 +3101,9 @@ checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "percent-encoding" -version = "2.3.1" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "petgraph" @@ -3120,12 +3117,12 @@ dependencies = [ [[package]] name = "petgraph" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54acf3a685220b533e437e264e4d932cfbdc4cc7ec0cd232ed73c08d03b8a7ca" +checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" dependencies = [ "fixedbitset 0.5.7", - "hashbrown", + "hashbrown 0.15.5", "indexmap", ] @@ -3175,8 +3172,9 @@ checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "player" -version = "26.0.0" +version = "27.0.4" dependencies = [ + "bytemuck", "env_logger", "log", "raw-window-handle 0.6.2", @@ -3217,30 +3215,29 @@ dependencies = [ [[package]] name = "png" -version = "0.17.16" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.4", "crc32fast", "fdeflate", "flate2", - "miniz_oxide 0.8.9", + "miniz_oxide", ] [[package]] name = "polling" -version = "3.8.0" +version = "3.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" dependencies = [ "cfg-if", "concurrent-queue", "hermit-abi", "pin-project-lite", - "rustix 1.0.7", - "tracing", - "windows-sys 0.59.0", + "rustix 1.1.2", + "windows-sys 0.61.1", ] [[package]] @@ -3280,9 +3277,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +checksum = "84df19adbe5b5a0782edcab45899906947ab039ccf4573713735ee7de1e6b08a" dependencies = [ "zerovec", ] @@ -3304,9 +3301,9 @@ checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" [[package]] name = "prettyplease" -version = "0.2.35" +version = "0.2.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "061c1221631e079b26479d25bbf2275bfe5917ae8419cd7e34f13bfc2aa7539a" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", "syn", @@ -3314,9 +3311,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" dependencies = [ "toml_edit", ] @@ -3346,9 +3343,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.95" +version = "1.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" dependencies = [ "unicode-ident", ] @@ -3359,6 +3356,15 @@ version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +[[package]] +name = "pxfm" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83f9b339b02259ada5c0f4a389b7fb472f933aa17ce176fd2ad98f28bb401fde" +dependencies = [ + "num-traits", +] + [[package]] name = "quick-xml" version = "0.37.5" @@ -3370,9 +3376,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.40" +version = "1.0.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" dependencies = [ "proc-macro2", ] @@ -3433,9 +3439,9 @@ checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" [[package]] name = "rayon" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", @@ -3443,9 +3449,9 @@ dependencies = [ [[package]] name = "rayon-core" -version = "1.12.1" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", @@ -3471,62 +3477,47 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.5.13" +version = "0.5.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6" +checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", ] [[package]] name = "regex" -version = "1.11.1" +version = "1.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +checksum = "8b5288124840bee7b386bc413c487869b360b2b4ec421ea56425128692f2a82c" dependencies = [ "aho-corasick", "memchr", - "regex-automata 0.4.9", - "regex-syntax 0.8.5", + "regex-automata", + "regex-syntax", ] [[package]] name = "regex-automata" -version = "0.1.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" -dependencies = [ - "regex-syntax 0.6.29", -] - -[[package]] -name = "regex-automata" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +checksum = "833eb9ce86d40ef33cb1306d8accf7bc8ec2bfea4355cbdebb3df68b40925cad" dependencies = [ "aho-corasick", "memchr", - "regex-syntax 0.8.5", + "regex-syntax", ] [[package]] name = "regex-lite" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a49587ad06b26609c52e423de037e7f57f20d53535d66e08c695f347df952a" - -[[package]] -name = "regex-syntax" -version = "0.6.29" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1" +checksum = "943f41321c63ef1c92fd763bfe054d2668f7f225a5c29f0105903dc2fc04ba30" [[package]] name = "regex-syntax" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" +checksum = "caf4aa5b0f434c91fe5c7f1ecb6a5ece2130b02ad2a590589dda5146df959001" [[package]] name = "renderdoc-sys" @@ -3536,12 +3527,12 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "ron" -version = "0.10.1" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "beceb6f7bf81c73e73aeef6dd1356d9a1b2b4909e1f0fc3e59b034f9572d7b7f" +checksum = "db09040cc89e461f1a265139777a2bde7f8d8c67c4936f700c63ce3e2904d468" dependencies = [ "base64", - "bitflags 2.9.1", + "bitflags 2.9.4", "serde", "serde_derive", "unicode-ident", @@ -3549,11 +3540,12 @@ dependencies = [ [[package]] name = "rspirv" -version = "0.11.0+sdk-1.2.198" -source = "git+https://github.com/gfx-rs/rspirv?rev=b969f175d5663258b4891e44b76c1544da9661ab#b969f175d5663258b4891e44b76c1544da9661ab" +version = "0.12.0+sdk-1.3.268.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cf3a93856b6e5946537278df0d3075596371b1950ccff012f02b0f7eafec8d" dependencies = [ - "rustc-hash", - "spirv 0.2.0+sdk-1.2.198", + "rustc-hash 1.1.0", + "spirv", ] [[package]] @@ -3564,9 +3556,9 @@ checksum = "a157657054ffe556d8858504af8a672a054a6e0bd9e8ee531059100c0fa11bb2" [[package]] name = "rustc-demangle" -version = "0.1.25" +version = "0.1.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" [[package]] name = "rustc-hash" @@ -3575,13 +3567,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] -name = "rustc_version" -version = "0.2.3" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver 0.9.0", -] +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustix" @@ -3589,7 +3578,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3598,22 +3587,22 @@ dependencies = [ [[package]] name = "rustix" -version = "1.0.7" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "errno", "libc", - "linux-raw-sys 0.9.4", + "linux-raw-sys 0.11.0", "windows-sys 0.59.0", ] [[package]] name = "rustversion" -version = "1.0.21" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" @@ -3670,63 +3659,38 @@ dependencies = [ [[package]] name = "semver" -version = "0.9.0" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" dependencies = [ "serde", + "serde_core", ] -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - [[package]] name = "serde" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] -name = "serde-untagged" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "299d9c19d7d466db4ab10addd5703e4c615dec2a5a16dbbafe191045e87ee66e" -dependencies = [ - "erased-serde", - "serde", - "typeid", -] - -[[package]] -name = "serde-value" -version = "0.7.0" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "ordered-float 2.10.1", - "serde", + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.219" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", @@ -3735,37 +3699,38 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.140" +version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ "indexmap", "itoa", "memchr", "ryu", "serde", + "serde_core", ] [[package]] name = "serde_spanned" -version = "0.6.9" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +checksum = "5417783452c2be558477e104686f7de5dae53dba813c28435e0e70f82d9b04ee" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "serde_v8" -version = "0.247.0" +version = "0.264.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12bbfafb7b707cbed49d1eaf48f4aa41b5ff57f813d1a80f77244e6e2fa4507e" +checksum = "c34707712f3815e73e1c8319bba06e5bc105bb65fe812ea2e7279ffb905f6312" dependencies = [ "deno_error", "num-bigint", "serde", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "v8", ] @@ -3792,22 +3757,13 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" -version = "1.4.5" +version = "1.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" +checksum = "b2a4719bff48cee6b39d12c020eeb490953ad2443b7055bd0b21fca26bd8c28b" dependencies = [ "libc", ] -[[package]] -name = "simd-abstraction" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cadb29c57caadc51ff8346233b5cec1d240b68ce55cf1afc764818791876987" -dependencies = [ - "outref 0.1.0", -] - [[package]] name = "simd-adler32" version = "0.3.7" @@ -3816,9 +3772,9 @@ checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" [[package]] name = "slab" -version = "0.4.10" +version = "0.4.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" [[package]] name = "slotmap" @@ -3841,7 +3797,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "calloop 0.12.4", "calloop-wayland-source 0.2.0", "cursor-icon", @@ -3866,7 +3822,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "calloop 0.13.0", "calloop-wayland-source 0.3.0", "cursor-icon", @@ -3879,8 +3835,8 @@ dependencies = [ "wayland-client", "wayland-csd-frame", "wayland-cursor", - "wayland-protocols 0.32.8", - "wayland-protocols-wlr 0.3.8", + "wayland-protocols 0.32.9", + "wayland-protocols-wlr 0.3.9", "wayland-scanner", "xkeysym", ] @@ -3896,27 +3852,26 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.10" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "sourcemap" -version = "8.0.1" +version = "9.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "208d40b9e8cad9f93613778ea295ed8f3c2b1824217c6cfc7219d3f6f45b96d4" +checksum = "e22afbcb92ce02d23815b9795523c005cb9d3c214f8b7a66318541c240ea7935" dependencies = [ - "base64-simd 0.7.0", + "base64-simd", "bitvec", "data-encoding", "debugid", "if_chain", - "rustc-hash", - "rustc_version", + "rustc-hash 2.1.1", "serde", "serde_json", "unicode-id-start", @@ -3932,21 +3887,13 @@ dependencies = [ "lock_api", ] -[[package]] -name = "spirv" -version = "0.2.0+sdk-1.2.198" -source = "git+https://github.com/gfx-rs/rspirv?rev=b969f175d5663258b4891e44b76c1544da9661ab#b969f175d5663258b4891e44b76c1544da9661ab" -dependencies = [ - "bitflags 1.3.2", -] - [[package]] name = "spirv" version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "serde", ] @@ -3970,9 +3917,9 @@ checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" [[package]] name = "stringcase" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04028eeb851ed08af6aba5caa29f2d59a13ed168cee4d6bd753aeefcf1d636b0" +checksum = "72abeda133c49d7bddece6c154728f83eec8172380c80ab7096da9487e20d27c" [[package]] name = "strsim" @@ -3982,53 +3929,30 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" -dependencies = [ - "strum_macros 0.25.3", -] - -[[package]] -name = "strum" -version = "0.27.1" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" dependencies = [ - "strum_macros 0.27.1", + "strum_macros", ] [[package]] name = "strum_macros" -version = "0.25.3" +version = "0.27.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" dependencies = [ - "heck 0.4.1", + "heck", "proc-macro2", "quote", - "rustversion", - "syn", -] - -[[package]] -name = "strum_macros" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" -dependencies = [ - "heck 0.5.0", - "proc-macro2", - "quote", - "rustversion", "syn", ] [[package]] name = "syn" -version = "2.0.104" +version = "2.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40" +checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6" dependencies = [ "proc-macro2", "quote", @@ -4048,9 +3972,9 @@ dependencies = [ [[package]] name = "sys_traits" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc4707edf3196e8037ee45018d1bb1bfb233b0e4fc440fa3d3f25bc69bfdaf26" +checksum = "4f74a2c95f72e36fa6bd04a40d15623a9904bab1cc2fa6c6135b09d774a65088" dependencies = [ "sys_traits_macros", ] @@ -4098,11 +4022,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" dependencies = [ - "thiserror-impl 2.0.12", + "thiserror-impl 2.0.17", ] [[package]] @@ -4118,9 +4042,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.12" +version = "2.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" dependencies = [ "proc-macro2", "quote", @@ -4193,9 +4117,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.46.1" +version = "1.47.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17" +checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038" dependencies = [ "backtrace", "bytes", @@ -4208,7 +4132,7 @@ dependencies = [ "slab", "socket2", "tokio-macros", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -4224,44 +4148,54 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.23" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362" +checksum = "00e5e5d9bf2475ac9d4f0d9edab68cc573dc2fd644b0dba36b0c30a92dd9eaa0" dependencies = [ - "serde", + "indexmap", + "serde_core", "serde_spanned", "toml_datetime", - "toml_edit", + "toml_parser", + "toml_writer", + "winnow", ] [[package]] name = "toml_datetime" -version = "0.6.11" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c" +checksum = "32f1085dec27c2b6632b04c80b3bb1b4300d6495d1e129693bdda7d91e72eec1" dependencies = [ - "serde", + "serde_core", ] [[package]] name = "toml_edit" -version = "0.22.27" +version = "0.23.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a" +checksum = "f3effe7c0e86fdff4f69cdd2ccc1b96f933e24811c5441d44904e8683e27184b" dependencies = [ "indexmap", - "serde", - "serde_spanned", "toml_datetime", - "toml_write", + "toml_parser", "winnow", ] [[package]] -name = "toml_write" -version = "0.1.2" +name = "toml_parser" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cf893c33be71572e0e9aa6dd15e6677937abd686b066eac3f8cd3531688a627" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" +checksum = "d163a63c116ce562a22cda521fcc4d79152e7aba014456fb5eb442f6d6a10109" [[package]] name = "tracing" @@ -4296,14 +4230,14 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.19" +version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" dependencies = [ "matchers", "nu-ansi-term", "once_cell", - "regex", + "regex-automata", "sharded-slab", "smallvec", "thread_local", @@ -4325,9 +4259,9 @@ dependencies = [ [[package]] name = "tracy-client-sys" -version = "0.26.0" +version = "0.26.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f9612d9503675b07b244922ea6f6f3cdd88c43add1b3498084613fc88cdf69d" +checksum = "319c70195101a93f56db4c74733e272d720768e13471f400c78406a326b172b0" dependencies = [ "cc", "windows-targets 0.52.6", @@ -4335,9 +4269,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.105" +version = "1.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c9bf9513a2f4aeef5fdac8677d7d349c79fdbcc03b9c86da6e9d254f1e43be2" +checksum = "0ded9fdb81f30a5708920310bfcd9ea7482ff9cba5f54601f7a19a877d5c2392" dependencies = [ "glob", "serde", @@ -4355,10 +4289,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" [[package]] -name = "typeid" -version = "1.0.3" +name = "typewit" +version = "1.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" +checksum = "f8c1ae7cc0fdb8b842d65d127cb981574b0d2b249b74d1c7a2986863dc134f71" [[package]] name = "unic-char-property" @@ -4403,15 +4337,15 @@ dependencies = [ [[package]] name = "unicode-id-start" -version = "1.3.1" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f322b60f6b9736017344fa0635d64be2f458fbc04eef65f6be22976dd1ffd5b" +checksum = "81b79ad29b5e19de4260020f8919b443b2ef0277d242ce532ec7b7a2cc8b6007" [[package]] name = "unicode-ident" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" [[package]] name = "unicode-segmentation" @@ -4437,11 +4371,17 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "323402cff2dd658f39ca17c789b502021b3f18707c91cdf22e3838e1b4023817" +[[package]] +name = "unty" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d49784317cd0d1ee7ec5c716dd598ec5b4483ea832a2dced265471cc0f690ae" + [[package]] name = "url" -version = "2.5.4" +version = "2.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", "idna", @@ -4475,9 +4415,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.17.0" +version = "1.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" +checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" dependencies = [ "getrandom 0.3.3", "js-sys", @@ -4487,17 +4427,16 @@ dependencies = [ [[package]] name = "v8" -version = "134.5.0" +version = "137.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21c7a224a7eaf3f98c1bad772fbaee56394dce185ef7b19a2e0ca5e3d274165d" +checksum = "33995a1fee055ff743281cde33a41f0d618ee0bdbe8bdf6859e11864499c2595" dependencies = [ "bindgen", - "bitflags 2.9.1", + "bitflags 2.9.4", "fslock", "gzip-header", "home", - "miniz_oxide 0.7.4", - "once_cell", + "miniz_oxide", "paste", "which 6.0.3", ] @@ -4514,6 +4453,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "virtue" +version = "0.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "051eb1abcf10076295e815102942cc58f9d5e3b4560e46e53c21e8ff6f3af7b1" + [[package]] name = "vsimd" version = "0.8.0" @@ -4538,30 +4483,40 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasi" -version = "0.14.2+wasi-0.2.4" +version = "0.14.7+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +checksum = "883478de20367e224c0090af9cf5f9fa85bed63a95c1abf3afc5c083ebc06e8c" dependencies = [ - "wit-bindgen-rt", + "wasip2", +] + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", ] [[package]] name = "wasm-bindgen" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +checksum = "c1da10c01ae9f1ae40cbfac0bac3b1e724b320abfcf52229f80b547c0d250e2d" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +checksum = "671c9a5a66f49d8a47345ab942e2cb93c7d1d0339065d4f8139c486121b43b19" dependencies = [ "bumpalo", "log", @@ -4573,9 +4528,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.50" +version = "0.4.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +checksum = "7e038d41e478cc73bae0ff9b36c60cff1c98b8f38f8d7e8061e79ee63608ac5c" dependencies = [ "cfg-if", "js-sys", @@ -4586,9 +4541,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +checksum = "7ca60477e4c59f5f2986c50191cd972e3a50d8a95603bc9434501cf156a9a119" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4596,9 +4551,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +checksum = "9f07d2f20d4da7b26400c9f4a0511e6e0345b040694e8a75bd41d578fa4421d7" dependencies = [ "proc-macro2", "quote", @@ -4609,18 +4564,18 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.100" +version = "0.2.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +checksum = "bad67dc8b2a1a6e5448428adec4c3e84c43e561d8c9ee8a9e5aabeb193ec41d1" dependencies = [ "unicode-ident", ] [[package]] name = "wasm-bindgen-test" -version = "0.3.50" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c8d5e33ca3b6d9fa3b4676d774c5778031d27a578c2b007f905acf816152c3" +checksum = "4e381134e148c1062f965a42ed1f5ee933eef2927c3f70d1812158f711d39865" dependencies = [ "js-sys", "minicov", @@ -4631,9 +4586,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-test-macro" -version = "0.3.50" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17d5042cc5fa009658f9a7333ef24291b1291a25b6382dd68862a7f3b969f69b" +checksum = "b673bca3298fe582aeef8352330ecbad91849f85090805582400850f8270a2e8" dependencies = [ "proc-macro2", "quote", @@ -4642,23 +4597,23 @@ dependencies = [ [[package]] name = "wasm_dep_analyzer" -version = "0.2.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eeee3bdea6257cc36d756fa745a70f9d393571e47d69e0ed97581676a5369ca" +checksum = "a10e6b67c951a84de7029487e0e0a496860dae49f6699edd279d5ff35b8fbf54" dependencies = [ "deno_error", - "thiserror 2.0.12", + "thiserror 2.0.17", ] [[package]] name = "wayland-backend" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.44", + "rustix 1.1.2", "scoped-tls", "smallvec", "wayland-sys", @@ -4666,12 +4621,12 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" dependencies = [ - "bitflags 2.9.1", - "rustix 0.38.44", + "bitflags 2.9.4", + "rustix 1.1.2", "wayland-backend", "wayland-scanner", ] @@ -4682,18 +4637,18 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.10" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a65317158dec28d00416cb16705934070aef4f8393353d41126c54264ae0f182" +checksum = "447ccc440a881271b19e9989f75726d60faa09b95b0200a9b7eb5cc47c3eeb29" dependencies = [ - "rustix 0.38.44", + "rustix 1.1.2", "wayland-client", "xcursor", ] @@ -4704,7 +4659,7 @@ version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4712,11 +4667,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.8" +version = "0.32.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4728,7 +4683,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-protocols 0.31.2", @@ -4737,14 +4692,14 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fd38cdad69b56ace413c6bcc1fbf5acc5e2ef4af9d5f8f1f9570c0c83eae175" +checksum = "a07a14257c077ab3279987c4f8bb987851bf57081b93710381daea94f2c2c032" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.8", + "wayland-protocols 0.32.9", "wayland-scanner", ] @@ -4754,7 +4709,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", "wayland-protocols 0.31.2", @@ -4763,22 +4718,22 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" +checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.8", + "wayland-protocols 0.32.9", "wayland-scanner", ] [[package]] name = "wayland-scanner" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" dependencies = [ "proc-macro2", "quick-xml", @@ -4787,9 +4742,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.6" +version = "0.31.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" dependencies = [ "dlib", "log", @@ -4799,9 +4754,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.77" +version = "0.3.81" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +checksum = "9367c417a924a74cae129e6a2ae3b47fabb1f8995595ab474029da749a8be120" dependencies = [ "js-sys", "wasm-bindgen", @@ -4829,15 +4784,15 @@ dependencies = [ [[package]] name = "wgpu" -version = "26.0.0" +version = "27.0.4" dependencies = [ "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.4", "bytemuck", "cfg-if", "cfg_aliases 0.2.1", "document-features", - "hashbrown", + "hashbrown 0.16.0", "js-sys", "log", "naga", @@ -4857,12 +4812,13 @@ dependencies = [ [[package]] name = "wgpu-benchmark" -version = "26.0.0" +version = "27.0.4" dependencies = [ - "bincode", + "bincode 2.0.1", "bytemuck", "criterion", "naga", + "naga-test", "nanorand 0.8.0", "pollster", "profiling", @@ -4873,16 +4829,16 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "26.0.0" +version = "27.0.4" dependencies = [ "arrayvec", - "bit-set 0.8.0", - "bit-vec 0.8.0", - "bitflags 2.9.1", + "bit-set", + "bit-vec", + "bitflags 2.9.4", "bytemuck", "cfg_aliases 0.2.1", "document-features", - "hashbrown", + "hashbrown 0.16.0", "indexmap", "log", "naga", @@ -4892,10 +4848,10 @@ dependencies = [ "profiling", "raw-window-handle 0.6.2", "ron", - "rustc-hash", + "rustc-hash 1.1.0", "serde", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-wasm", @@ -4906,28 +4862,28 @@ dependencies = [ [[package]] name = "wgpu-core-deps-apple" -version = "26.0.0" +version = "27.0.4" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-emscripten" -version = "26.0.0" +version = "27.0.4" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-wasm" -version = "26.0.0" +version = "27.0.4" dependencies = [ "wgpu-hal", ] [[package]] name = "wgpu-core-deps-windows-linux-android" -version = "26.0.0" +version = "27.0.4" dependencies = [ "wgpu-hal", ] @@ -4949,7 +4905,7 @@ dependencies = [ "env_logger", "pollster", "wgpu", - "winit 0.30.11", + "winit 0.30.12", ] [[package]] @@ -4962,7 +4918,7 @@ dependencies = [ [[package]] name = "wgpu-examples" -version = "26.0.0" +version = "27.0.4" dependencies = [ "bytemuck", "cfg-if", @@ -4972,6 +4928,7 @@ dependencies = [ "env_logger", "fern", "flume", + "getrandom 0.3.3", "glam", "ktx2", "log", @@ -4993,13 +4950,13 @@ dependencies = [ [[package]] name = "wgpu-hal" -version = "26.0.0" +version = "27.0.4" dependencies = [ "android_system_properties", "arrayvec", "ash", - "bit-set 0.8.0", - "bitflags 2.9.1", + "bit-set", + "bitflags 2.9.4", "block", "bytemuck", "cfg-if", @@ -5014,7 +4971,7 @@ dependencies = [ "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hashbrown", + "hashbrown 0.16.0", "js-sys", "khronos-egl", "libc", @@ -5025,7 +4982,8 @@ dependencies = [ "naga", "ndk-sys 0.6.0+11769913", "objc", - "ordered-float 5.0.0", + "once_cell", + "ordered-float", "parking_lot", "portable-atomic", "portable-atomic-util", @@ -5034,9 +4992,9 @@ dependencies = [ "raw-window-handle 0.5.2", "raw-window-handle 0.6.2", "renderdoc-sys", - "rustc-hash", + "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.12", + "thiserror 2.0.17", "wasm-bindgen", "web-sys", "wgpu-types", @@ -5047,12 +5005,12 @@ dependencies = [ [[package]] name = "wgpu-info" -version = "26.0.0" +version = "27.0.4" dependencies = [ "anyhow", - "bitflags 2.9.1", + "bitflags 2.9.4", "env_logger", - "hashbrown", + "hashbrown 0.16.0", "pico-args", "serde", "serde_json", @@ -5061,21 +5019,21 @@ dependencies = [ [[package]] name = "wgpu-macros" -version = "26.0.0" +version = "27.0.4" dependencies = [ - "heck 0.5.0", + "heck", "quote", "syn", ] [[package]] name = "wgpu-test" -version = "26.0.0" +version = "27.0.4" dependencies = [ "anyhow", "approx", "arrayvec", - "bitflags 2.9.1", + "bitflags 2.9.4", "bytemuck", "cargo_metadata", "cfg-if", @@ -5098,7 +5056,7 @@ dependencies = [ "profiling", "serde", "serde_json", - "strum 0.27.1", + "strum", "trybuild", "wasm-bindgen", "wasm-bindgen-futures", @@ -5111,15 +5069,15 @@ dependencies = [ [[package]] name = "wgpu-types" -version = "26.0.0" +version = "27.0.4" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "bytemuck", "js-sys", "log", "serde", "serde_json", - "thiserror 2.0.12", + "thiserror 2.0.17", "web-sys", ] @@ -5154,7 +5112,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" dependencies = [ "env_home", - "rustix 1.0.7", + "rustix 1.1.2", "winsafe", ] @@ -5176,9 +5134,9 @@ checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ "windows-sys 0.59.0", ] @@ -5208,7 +5166,7 @@ dependencies = [ "windows-collections", "windows-core 0.61.2", "windows-future", - "windows-link", + "windows-link 0.1.3", "windows-numerics", ] @@ -5240,9 +5198,9 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" dependencies = [ - "windows-implement 0.60.0", - "windows-interface 0.59.1", - "windows-link", + "windows-implement 0.60.1", + "windows-interface 0.59.2", + "windows-link 0.1.3", "windows-result 0.3.4", "windows-strings 0.4.2", ] @@ -5254,7 +5212,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", - "windows-link", + "windows-link 0.1.3", "windows-threading", ] @@ -5271,9 +5229,9 @@ dependencies = [ [[package]] name = "windows-implement" -version = "0.60.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +checksum = "edb307e42a74fb6de9bf3a02d9712678b22399c87e6fa869d6dfcd8c1b7754e0" dependencies = [ "proc-macro2", "quote", @@ -5293,9 +5251,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.59.1" +version = "0.59.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +checksum = "c0abd1ddbc6964ac14db11c7213d6532ef34bd9aa042c2e5935f59d7908b46a5" dependencies = [ "proc-macro2", "quote", @@ -5308,6 +5266,12 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +[[package]] +name = "windows-link" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45e46c0661abb7180e7b9c281db115305d49ca1709ab8242adf09666d2173c65" + [[package]] name = "windows-numerics" version = "0.2.0" @@ -5315,7 +5279,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" dependencies = [ "windows-core 0.61.2", - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -5333,7 +5297,7 @@ version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -5352,7 +5316,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -5397,7 +5361,16 @@ version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.2", + "windows-targets 0.53.4", +] + +[[package]] +name = "windows-sys" +version = "0.61.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f109e41dd4a3c848907eb83d5a42ea98b3769495597450cf6d153507b166f0f" +dependencies = [ + "windows-link 0.2.0", ] [[package]] @@ -5448,10 +5421,11 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.53.2" +version = "0.53.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66f69fcc9ce11da9966ddb31a40968cad001c5bedeb5c2b82ede4253ab48aef" +checksum = "2d42b7b7f66d2a06854650af09cfdf8713e427a439c97ad65a6375318033ac4b" dependencies = [ + "windows-link 0.2.0", "windows_aarch64_gnullvm 0.53.0", "windows_aarch64_msvc 0.53.0", "windows_i686_gnu 0.53.0", @@ -5468,7 +5442,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" dependencies = [ - "windows-link", + "windows-link 0.1.3", ] [[package]] @@ -5660,7 +5634,7 @@ dependencies = [ "ahash", "android-activity 0.5.2", "atomic-waker", - "bitflags 2.9.1", + "bitflags 2.9.4", "bytemuck", "calloop 0.12.4", "cfg_aliases 0.1.1", @@ -5702,14 +5676,14 @@ dependencies = [ [[package]] name = "winit" -version = "0.30.11" +version = "0.30.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4409c10174df8779dc29a4788cac85ed84024ccbc1743b776b21a520ee1aaf4" +checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" dependencies = [ "ahash", "android-activity 0.6.0", "atomic-waker", - "bitflags 2.9.1", + "bitflags 2.9.4", "block2 0.5.1", "bytemuck", "calloop 0.13.0", @@ -5742,8 +5716,8 @@ dependencies = [ "wasm-bindgen-futures", "wayland-backend", "wayland-client", - "wayland-protocols 0.32.8", - "wayland-protocols-plasma 0.3.8", + "wayland-protocols 0.32.9", + "wayland-protocols-plasma 0.3.9", "web-sys", "web-time 1.1.0", "windows-sys 0.52.0", @@ -5754,9 +5728,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.11" +version = "0.7.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" dependencies = [ "memchr", ] @@ -5768,13 +5742,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" [[package]] -name = "wit-bindgen-rt" -version = "0.39.0" +name = "wit-bindgen" +version = "0.46.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" -dependencies = [ - "bitflags 2.9.1", -] +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" [[package]] name = "writeable" @@ -5804,24 +5775,24 @@ dependencies = [ [[package]] name = "x11rb" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", "libloading", "once_cell", - "rustix 0.38.44", + "rustix 1.1.2", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.1" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" [[package]] name = "xcursor" @@ -5835,7 +5806,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.9.1", + "bitflags 2.9.4", "dlib", "log", "once_cell", @@ -5850,9 +5821,9 @@ checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] name = "xml-rs" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a62ce76d9b56901b19a74f19431b0d8b3bc7ca4ad685a746dfd78ca8f4fc6bda" +checksum = "6fd8403733700263c6eb89f192880191f1b83e332f7a20371ddcf421c4a337c7" [[package]] name = "xshell" @@ -5895,18 +5866,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039dd0d3c310cf05de012d8a39ff557cb0d23087fd44cad61df08fc31907a2f" +checksum = "0894878a5fa3edfd6da3f88c4805f4c8558e2b996227a3d864f47fe11e38282c" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.26" +version = "0.8.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ecf5b4cc5364572d7f4c329661bcc82724222973f2cab6f050a4e5c22f75181" +checksum = "88d2b8d9c68ad2b9e4340d7832716a4d21a22a1154777ad56ea55c51a9cf3831" dependencies = [ "proc-macro2", "quote", @@ -5947,9 +5918,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b" dependencies = [ "yoke", "zerofrom", diff --git a/Cargo.toml b/Cargo.toml index bab8e735098..069e928b81b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,6 +10,7 @@ members = [ "examples/standalone/*", "lock-analyzer", "naga-cli", + "naga-test", "naga", "naga/fuzz", "naga/hlsl-snapshots", @@ -32,6 +33,7 @@ default-members = [ "examples/standalone/*", "lock-analyzer", "naga-cli", + "naga-test", "naga", "naga/fuzz", "naga/hlsl-snapshots", @@ -54,17 +56,18 @@ ref_as_ptr = "warn" [workspace.package] edition = "2021" -rust-version = "1.84" +rust-version = "1.88" keywords = ["graphics"] license = "MIT OR Apache-2.0" homepage = "https://wgpu.rs/" repository = "https://github.com/gfx-rs/wgpu" -version = "26.0.0" +version = "27.0.4" authors = ["gfx-rs developers"] [workspace.dependencies] -naga = { version = "26.0.0", path = "./naga" } -wgpu = { version = "26.0.0", path = "./wgpu", default-features = false, features = [ +naga = { version = "27.0.0", path = "./naga" } +naga-test = { path = "./naga-test" } +wgpu = { version = "27.0.0", path = "./wgpu", default-features = false, features = [ "serde", "wgsl", "vulkan", @@ -74,28 +77,28 @@ wgpu = { version = "26.0.0", path = "./wgpu", default-features = false, features "static-dxc", "noop", # This should be removed if we ever have non-test crates that depend on wgpu ] } -wgpu-core = { version = "26.0.0", path = "./wgpu-core" } -wgpu-hal = { version = "26.0.0", path = "./wgpu-hal" } -wgpu-macros = { version = "26.0.0", path = "./wgpu-macros" } -wgpu-test = { version = "26.0.0", path = "./tests" } -wgpu-types = { version = "26.0.0", path = "./wgpu-types", default-features = false } +wgpu-core = { version = "27.0.0", path = "./wgpu-core" } +wgpu-hal = { version = "27.0.0", path = "./wgpu-hal" } +wgpu-macros = { version = "27.0.0", path = "./wgpu-macros" } +wgpu-test = { version = "27.0.0", path = "./tests" } +wgpu-types = { version = "27.0.0", path = "./wgpu-types", default-features = false } # These _cannot_ have a version specified. If it does, crates.io will look # for a version of the package on crates when we publish naga. Path dependencies # are allowed through though. hlsl-snapshots = { path = "naga/hlsl-snapshots" } -wgpu-core-deps-windows-linux-android = { version = "26.0.0", path = "./wgpu-core/platform-deps/windows-linux-android" } -wgpu-core-deps-apple = { version = "26.0.0", path = "./wgpu-core/platform-deps/apple" } -wgpu-core-deps-wasm = { version = "26.0.0", path = "./wgpu-core/platform-deps/wasm" } -wgpu-core-deps-emscripten = { version = "26.0.0", path = "./wgpu-core/platform-deps/emscripten" } +wgpu-core-deps-windows-linux-android = { version = "27.0.0", path = "./wgpu-core/platform-deps/windows-linux-android" } +wgpu-core-deps-apple = { version = "27.0.0", path = "./wgpu-core/platform-deps/apple" } +wgpu-core-deps-wasm = { version = "27.0.0", path = "./wgpu-core/platform-deps/wasm" } +wgpu-core-deps-emscripten = { version = "27.0.0", path = "./wgpu-core/platform-deps/emscripten" } anyhow = { version = "1.0.87", default-features = false } approx = "0.5" -arbitrary = "1.4" +arbitrary = "1.4.2" argh = "0.1.13" arrayvec = { version = "0.7.1", default-features = false } -bincode = "1" +bincode = "2" bit-set = { version = "0.8", default-features = false } bit-vec = { version = "0.8", default-features = false } bitflags = "2.9" @@ -103,35 +106,36 @@ bytemuck = { version = "1.22", features = [ "extern_crate_alloc", "min_const_generics", ] } -cargo_metadata = "0.20" +cargo_metadata = "0.23" cfg_aliases = "0.2.1" cfg-if = "1" -criterion = "0.6" +criterion = "0.7" codespan-reporting = { version = "0.12", default-features = false } -ctor = "0.4" +ctor = "0.5" diff = "0.1" document-features = "0.2.10" -encase = "0.11" +encase = "0.12" env_logger = { version = "0.11", default-features = false } fern = "0.7" flume = "0.11" futures-lite = "2" -glam = "0.30" +getrandom = "0.3" +glam = "0.30.7" glob = "0.3" half = { version = "2.5", default-features = false } # We require 2.5 to have `Arbitrary` support. -hashbrown = { version = "0.15", default-features = false, features = [ +hashbrown = { version = "0.16", default-features = false, features = [ "default-hasher", "inline-more", ] } heck = "0.5" hexf-parse = "0.2" image = { version = "0.25", default-features = false, features = ["png"] } -indexmap = { version = "2.7", default-features = false } +indexmap = { version = "2.8", default-features = false } indicatif = "0.18" itertools = { version = "0.14" } jobserver = "0.1" ktx2 = "0.4" -libc = { version = "0.2.168", default-features = false } +libc = { version = "0.2.171", default-features = false } # See https://github.com/rust-fuzz/libfuzzer/issues/126 libfuzzer-sys = ">0.4.0,<=0.4.7" libloading = "0.8" @@ -149,7 +153,7 @@ obj = "0.10" # NOTE: once_cell/std is *required* for some commonly-used features, selecting this per crate once_cell = { version = "1.21", default-features = false } # Firefox has 3.4.0 vendored, so we allow that version in our dependencies -ordered-float = { version = ">=3, <=5.0", default-features = false } +ordered-float = { version = ">=3, <6.0", default-features = false } parking_lot = "0.12.3" petgraph = { version = "0.8", default-features = false } pico-args = { version = "0.5", features = [ @@ -157,36 +161,36 @@ pico-args = { version = "0.5", features = [ "short-space-opt", "combined-flags", ] } -png = "0.17.6" +png = "0.18" pollster = "0.4" portable-atomic = "1.8" portable-atomic-util = "0.2.4" pp-rs = "0.2.1" -profiling = { version = "1", default-features = false } +profiling = { version = "1.0.1", default-features = false } quote = "1.0.38" raw-window-handle = { version = "0.6.2", default-features = false } -rwh_05 = { version = "0.5.2", package = "raw-window-handle" } # temporary compatibility for glutin-winit +rwh_05 = { version = "0.5.2", package = "raw-window-handle" } # temporary compatibility for glutin-winit rayon = "1.3" regex-lite = "0.1" renderdoc-sys = "1" -rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" } -ron = "0.10" +rspirv = "0.12" +ron = "0.11" # NOTE: rustc-hash v2 is a completely different hasher with different performance characteristics # see discussion here (including with some other alternatives): https://github.com/gfx-rs/wgpu/issues/6999 # (using default-features = false to support no-std build, avoiding any extra features that may require std::collections) rustc-hash = { version = "1.1", default-features = false } -serde_json = "1.0.118" +serde_json = "1.0.143" serde = { version = "1.0.219", default-features = false } shell-words = "1" -smallvec = "1.9" +smallvec = "1.14" spirv = "0.3" static_assertions = "1.1" -strum = { version = "0.27", default-features = false, features = ["derive"] } +strum = { version = "0.27.1", default-features = false, features = ["derive"] } syn = "2.0.98" -toml = "0.8.9" +toml = "0.9.0" trybuild = "1" tracy-client = "0.18" -thiserror = { version = "2.0.3", default-features = false } +thiserror = { version = "2.0.12", default-features = false } unicode-ident = "1.0.5" walkdir = "2.3" winit = { version = "0.29", features = ["android-native-activity"] } @@ -232,15 +236,16 @@ web-sys = { version = "0.3.77", default-features = false } web-time = "1.1.0" # deno dependencies -deno_console = "0.192.0" -deno_core = "0.338.0" -deno_url = "0.192.0" -deno_web = "0.224.0" -deno_webidl = "0.192.0" -deno_webgpu = { version = "0.157.0", path = "./deno_webgpu" } -deno_unsync = "0.4.2" -deno_error = "0.5.5" -tokio = "1.39" +deno_console = "0.214.0" +deno_core = "0.355.0" +deno_features = "0.11.0" +deno_url = "0.214.0" +deno_web = "0.245.0" +deno_webidl = "0.214.0" +deno_webgpu = { version = "0.181.0", path = "./deno_webgpu" } +deno_unsync = "0.4.4" +deno_error = "0.7.0" +tokio = "1.45.1" termcolor = "1.1.3" # android dependencies diff --git a/GOVERNANCE.md b/GOVERNANCE.md index 73205cc41f5..b3e7a4086a3 100644 --- a/GOVERNANCE.md +++ b/GOVERNANCE.md @@ -1,15 +1,15 @@ -The **WGPU project** is a set of open-source libraries that _enables application +The **wgpu project** is a set of open-source libraries that _enables application authors to write portable and performant graphics programs_. It was originally conceived to provide an implementation of WebGPU for Firefox as the standard evolved, and settled into something that could be shipped on all web browsers. -WGPU has also enjoyed much contribution and use from other projects that require +wgpu has also enjoyed much contribution and use from other projects that require graphics programming. We expect that these sorts of users will continue for the lifetime of project, and we embrace these contributors' needs and effort as the -lifeblood of WGPU. +lifeblood of wgpu. ## Mission -The WGPU community seeks to realize the following directives through the +The wgpu community seeks to realize the following directives through the project: it… 1. …provides libraries for the WebGPU API that… @@ -28,7 +28,7 @@ project: it… ## Decision-making -The WGPU community's decision-making is influenced by the following +The wgpu community's decision-making is influenced by the following groups: * Community leadership: @@ -38,18 +38,18 @@ groups: * Firefox's WebGPU team (@jimblandy, @nical, @teoxoy, @ErichDonGubler, and others) * Deno's WebGPU contributors (@crowlKats) -* Other users that ship applications based on WGPU +* Other users that ship applications based on wgpu It is no coincidence that these groups correspond to the historically most -active and consistent contributors. In general, WGPU's community structure is +active and consistent contributors. In general, wgpu's community structure is meritocratic: social influence is granted proportionate to groups' contribution -to and stake in WGPU's mission. +to and stake in wgpu's mission. These decision-making groups meet together regularly to discuss issues of -importance to the community, with a focus on WGPU's [mission](#Mission). +importance to the community, with a focus on wgpu's [mission](#Mission). --- NOTE: The above is a snapshot of a perpetually changing state of affairs in the -WGPU community. It is not a binding contract between users and decision-makers -of the WGPU project. +wgpu community. It is not a binding contract between users and decision-makers +of the wgpu project. diff --git a/LICENSE.MIT b/LICENSE.MIT index 4699691b8ed..8d02e4dbd5f 100644 --- a/LICENSE.MIT +++ b/LICENSE.MIT @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2021 The gfx-rs developers +Copyright (c) 2025 The gfx-rs developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index ac984583e2e..94d4135843c 100644 --- a/README.md +++ b/README.md @@ -17,13 +17,13 @@ The API is based on the [WebGPU standard][webgpu]. It serves as the core of the | Docs | Examples | Changelog | |:---------------------:|:-------------------------:|:-----------------------:| -| [v26][rel-docs] | [v26][rel-examples] | [v26][rel-change] | +| [v27][rel-docs] | [v27][rel-examples] | [v27][rel-change] | | [`trunk`][trunk-docs] | [`trunk`][trunk-examples] | [`trunk`][trunk-change] | Contributors are welcome! See [CONTRIBUTING.md][contrib] for more information. [rel-docs]: https://docs.rs/wgpu/ -[rel-examples]: https://github.com/gfx-rs/wgpu/tree/v26/examples#readme +[rel-examples]: https://github.com/gfx-rs/wgpu/tree/v27/examples#readme [rel-change]: https://github.com/gfx-rs/wgpu/releases [trunk-docs]: https://wgpu.rs/doc/wgpu/ [trunk-examples]: https://github.com/gfx-rs/wgpu/tree/trunk/examples#readme @@ -149,8 +149,8 @@ On Linux, you can point to them using `LD_LIBRARY_PATH` environment. Due to complex dependants, we have two MSRV policies: -- `naga`, `wgpu-core`, `wgpu-hal`, and `wgpu-types`'s MSRV is **1.76**. -- The rest of the workspace has an MSRV of **1.84**. +- `naga`, `wgpu-core`, `wgpu-hal`, and `wgpu-types`'s MSRV is **1.82**. +- The rest of the workspace has an MSRV of **1.88**. It is enforced on CI (in "/.github/workflows/ci.yml") with the `CORE_MSRV` and `REPO_MSRV` variables. This version can only be upgraded in breaking releases, though we release a breaking version every three months. @@ -255,12 +255,11 @@ as the implementation catches up. Exactly which WGSL features `wgpu` supports depends on how you are using it: -- When running as native code, `wgpu` uses the [Naga][naga] crate +- When running as native code, `wgpu` uses [Naga][naga] to translate WGSL code into the shading language of your platform's native GPU API. - Naga has [a milestone][naga wgsl milestone] - for catching up to the WGSL specification, - but in general, there is no up-to-date summary - of the differences between Naga and the WGSL spec. + Naga is working on catching up to the WGSL specification, + with [bugs][naga bugs] tracking various issues, + but there is no concise summary of differences from the specification. - When running in a web browser (by compilation to WebAssembly) without the `"webgl"` feature enabled, @@ -274,8 +273,8 @@ Exactly which WGSL features `wgpu` supports depends on how you are using it: [webgpu spec]: https://www.w3.org/TR/webgpu/ [wgsl spec]: https://gpuweb.github.io/gpuweb/wgsl/ -[naga]: https://github.com/gfx-rs/naga/ -[naga wgsl milestone]: https://github.com/gfx-rs/naga/milestone/4 +[naga]: https://github.com/gfx-rs/wgpu/tree/trunk/naga/ +[naga bugs]: https://github.com/gfx-rs/wgpu/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22naga%22 ## Coordinate Systems diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000000..d6651d1e8e6 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,84 @@ +# wgpu Security Policy + +This document describes what is considered a security vulnerability in wgpu and +how vulnerabilities should be reported. + + +## Vulnerability Definition + +WebGPU introduces a different threat model than is sometimes applied to +GPU-related software. Unlike typical gaming or high-performance computing +applications, where the software accessing GPU APIs is proprietary or +obtained from a trusted developer, WebGPU makes GPU APIs available to +arbitrary web applications. In the threat model of the web, malicious +content should not be able to use the GPU APIs to access data or interfaces +outside the intended scope for interaction with web content. Therefore, `wgpu` +seeks to prevent undefined behavior and data leaks even when its API is +misused, and failures to do so may be considered vulnerabilities. (This is +also in accordance with the Rust principle of safe vs. unsafe code, since the +`wgpu` library exposes a safe API.) + +The wgpu maintainers have discretion in assigning a severity to individual +vulnerabilities. It is generally considered a high-severity vulnerability in +wgpu if JavaScript or WebAssembly code, running with privileges of ordinary web +content in a browser that is using wgpu to provide the WebGPU API to that +content, is able to: + +- Access data associated with native applications other than the user agent, + or associated with other web origins. +- Escape the applicable sandbox and run arbitrary code or call arbitrary system + APIs on the user agent host. +- Consume system resources to the point that it is difficult to recover + (e.g. by closing the web page). + +The wgpu Rust API offers some functionality, both supported and experimental, +that is not part of the WebGPU standard and is not made available in JavaScript +environments using wgpu. Associated vulnerabilities may be assigned lower +severity than vulnerabilities that apply to a wgpu-based WebGPU implementation +exposed to JavaScript. + + +## Supported Versions + +The wgpu project maintains security support for serious vulnerabilities in the +[most recent major release](https://github.com/gfx-rs/wgpu/releases). Fixes for +security vulnerabilities found shortly after the initial release of a major +version may also be provided for the previous major release. + +Mozilla provides security support for versions of wgpu used in [current +versions of Firefox](https://whattrainisitnow.com/). + +The version of wgpu that is active can be found in the Firefox repositories: + +- [release](https://github.com/mozilla-firefox/firefox/blob/release/gfx/wgpu_bindings/Cargo.toml), +- [beta](https://github.com/mozilla-firefox/firefox/blob/beta/gfx/wgpu_bindings/Cargo.toml), and +- [nightly](https://github.com/mozilla-firefox/firefox/blob/main/gfx/wgpu_bindings/Cargo.toml), + +We welcome reports of security vulnerabilities in any of these released +versions or in the latest code on the `trunk` branch. + + +## Reporting a Vulnerability + +Although not all vulnerabilities in wgpu will affect Firefox, Mozilla accepts +all vulnerability reports for wgpu and directs them appropriately. Additionally, +Mozilla serves as the CVE numbering authority for the wgpu project. + +To report a security problem with wgpu, create a bug in Mozilla's Bugzilla +instance in the +[Core :: Graphics :: WebGPU](https://bugzilla.mozilla.org/enter_bug.cgi?product=Core&component=Graphics%3A+WebGPU&groups=core-security&groups=gfx-core-security) +component. + +**IMPORTANT: For security issues, please make sure that you check the box +labelled "Many users could be harmed by this security problem".** We advise +that you check this option for anything that is potentially +security-relevant, including memory safety, crashes, race conditions, and +handling of confidential information. + +Review Mozilla's [guides on bug +reporting](https://bugzilla.mozilla.org/page.cgi?id=bug-writing.html) before +you open a bug. + +Mozilla operates a [bug bounty +program](https://www.mozilla.org/en-US/security/bug-bounty/). Some +vulnerabilities in this project may be eligible. diff --git a/benches/Cargo.toml b/benches/Cargo.toml index f0b70229adf..a1d09eea3a3 100644 --- a/benches/Cargo.toml +++ b/benches/Cargo.toml @@ -26,7 +26,7 @@ unexpected_cfgs = { level = "warn", check-cfg = [ ] } [dependencies] -bincode.workspace = true +bincode = { workspace = true, features = ["serde"] } bytemuck.workspace = true criterion.workspace = true naga = { workspace = true, features = [ @@ -41,6 +41,7 @@ naga = { workspace = true, features = [ "glsl-out", "wgsl-out", ] } +naga-test = { workspace = true, features = [] } nanorand.workspace = true pollster.workspace = true profiling.workspace = true diff --git a/benches/benches/wgpu-benchmark/bind_groups.rs b/benches/benches/wgpu-benchmark/bind_groups.rs index 6fb23d0a24d..cd2ad451a5a 100644 --- a/benches/benches/wgpu-benchmark/bind_groups.rs +++ b/benches/benches/wgpu-benchmark/bind_groups.rs @@ -87,7 +87,7 @@ fn run_bench(ctx: &mut Criterion) { for &count in count_list { group.throughput(Throughput::Elements(count as u64)); group.bench_with_input( - format!("{} Element Bind Group", count), + format!("{count} Element Bind Group"), &count, |b, &count| { b.iter_custom(|iters| { @@ -155,7 +155,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } diff --git a/benches/benches/wgpu-benchmark/computepass.rs b/benches/benches/wgpu-benchmark/computepass.rs index 9254547a1de..ed881641662 100644 --- a/benches/benches/wgpu-benchmark/computepass.rs +++ b/benches/benches/wgpu-benchmark/computepass.rs @@ -489,7 +489,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } @@ -538,7 +538,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } @@ -584,7 +584,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } diff --git a/benches/benches/wgpu-benchmark/main.rs b/benches/benches/wgpu-benchmark/main.rs index c087f1f8499..091ba8d0e58 100644 --- a/benches/benches/wgpu-benchmark/main.rs +++ b/benches/benches/wgpu-benchmark/main.rs @@ -47,6 +47,7 @@ impl DeviceState { required_features: adapter.features(), required_limits: adapter.limits(), memory_hints: wgpu::MemoryHints::Performance, + experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }, label: Some("Compute/RenderPass Device"), trace: wgpu::Trace::Off, })) diff --git a/benches/benches/wgpu-benchmark/renderpass.rs b/benches/benches/wgpu-benchmark/renderpass.rs index 946c32bfe40..07700d68fce 100644 --- a/benches/benches/wgpu-benchmark/renderpass.rs +++ b/benches/benches/wgpu-benchmark/renderpass.rs @@ -497,7 +497,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } @@ -544,7 +544,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } @@ -584,7 +584,7 @@ fn run_bench(ctx: &mut Criterion) { state .device_state .device - .poll(wgpu::PollType::Wait) + .poll(wgpu::PollType::wait_indefinitely()) .unwrap(); } diff --git a/benches/benches/wgpu-benchmark/resource_creation.rs b/benches/benches/wgpu-benchmark/resource_creation.rs index bbbfc3d2e31..7f23dd15c8d 100644 --- a/benches/benches/wgpu-benchmark/resource_creation.rs +++ b/benches/benches/wgpu-benchmark/resource_creation.rs @@ -61,7 +61,10 @@ fn run_bench(ctx: &mut Criterion) { drop(buffers); state.queue.submit([]); - state.device.poll(wgpu::PollType::Wait).unwrap(); + state + .device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); } duration diff --git a/benches/benches/wgpu-benchmark/shader.rs b/benches/benches/wgpu-benchmark/shader.rs index d54c566faf5..7e74df4f57c 100644 --- a/benches/benches/wgpu-benchmark/shader.rs +++ b/benches/benches/wgpu-benchmark/shader.rs @@ -1,57 +1,56 @@ use criterion::*; -use std::{fs, path::PathBuf, process::Command}; +use std::{fs, process::Command}; -struct Input { - filename: String, - size: u64, +const DIR_IN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../naga/tests/in"); + +use naga_test::*; + +struct InputWithInfo { + inner: Input, data: Vec, string: Option, + options: Parameters, module: Option, module_info: Option, } +impl From for InputWithInfo { + fn from(value: Input) -> Self { + let mut options = value.read_parameters(DIR_IN); + options.targets = Some(options.targets.unwrap_or(Targets::all())); + Self { + options, + inner: value, + data: Vec::new(), + string: None, + module: None, + module_info: None, + } + } +} +impl InputWithInfo { + fn filename(&self) -> &str { + self.inner.file_name.file_name().unwrap().to_str().unwrap() + } +} struct Inputs { - inner: Vec, + inner: Vec, } impl Inputs { #[track_caller] fn from_dir(folder: &str, extension: &str) -> Self { - let mut inputs = Vec::new(); - let read_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")) - .join(folder) - .read_dir() - .unwrap(); - - for file_entry in read_dir { - match file_entry { - Ok(entry) => match entry.path().extension() { - Some(ostr) if ostr == extension => { - let path = entry.path(); - - inputs.push(Input { - filename: path.to_string_lossy().into_owned(), - size: entry.metadata().unwrap().len(), - string: None, - data: vec![], - module: None, - module_info: None, - }); - } - _ => continue, - }, - Err(e) => { - eprintln!("Skipping file: {e:?}"); - continue; - } - } - } + let inputs: Vec = Input::files_in_dir(folder, &[extension], DIR_IN) + .map(|a| a.into()) + .collect(); Self { inner: inputs } } - fn bytes(&self) -> u64 { - self.inner.iter().map(|input| input.size).sum() + self.inner + .iter() + .map(|input| input.inner.bytes(DIR_IN)) + .sum() } fn load(&mut self) { @@ -60,7 +59,7 @@ impl Inputs { continue; } - input.data = fs::read(&input.filename).unwrap_or_default(); + input.data = fs::read(input.inner.input_path(DIR_IN)).unwrap_or_default(); } } @@ -85,6 +84,8 @@ impl Inputs { continue; } + parser.set_options((&input.options.wgsl_in).into()); + input.module = Some(parser.parse(input.string.as_ref().unwrap()).unwrap()); } } @@ -122,22 +123,22 @@ fn parse_glsl(stage: naga::ShaderStage, inputs: &Inputs) { }; for input in &inputs.inner { parser - .parse(&options, input.string.as_deref().unwrap()) + .parse(&options, &input.inner.read_source(DIR_IN, false)) .unwrap(); } } fn get_wgsl_inputs() -> Inputs { - let mut inputs = Inputs::from_dir("../naga/tests/in/wgsl", "wgsl"); + let mut inputs: Vec = Input::files_in_dir("wgsl", &["wgsl"], DIR_IN) + .map(|a| a.into()) + .collect(); // remove "large-source" tests, they skew the results - inputs - .inner - .retain(|input| !input.filename.contains("large-source")); + inputs.retain(|input| !input.filename().contains("large-source")); assert!(!inputs.is_empty()); - inputs + Inputs { inner: inputs } } fn frontends(c: &mut Criterion) { @@ -152,12 +153,22 @@ fn frontends(c: &mut Criterion) { let inputs_bin = inputs_wgsl .inner .iter() - .map(|input| bincode::serialize(&input.module.as_ref().unwrap()).unwrap()) + .map(|input| { + bincode::serde::encode_to_vec( + input.module.as_ref().unwrap(), + bincode::config::standard(), + ) + .unwrap() + }) .collect::>(); b.iter(move || { for input in inputs_bin.iter() { - bincode::deserialize::(input).unwrap(); + bincode::serde::decode_from_slice::( + input, + bincode::config::standard(), + ) + .unwrap(); } }); }); @@ -168,19 +179,20 @@ fn frontends(c: &mut Criterion) { let mut frontend = naga::front::wgsl::Frontend::new(); b.iter(|| { for input in &inputs_wgsl.inner { + frontend.set_options((&input.options.wgsl_in).into()); frontend.parse(input.string.as_ref().unwrap()).unwrap(); } }); }); - let inputs_spirv = Inputs::from_dir("../naga/tests/in/spv", "spvasm"); + let inputs_spirv = Inputs::from_dir("spv", "spvasm"); assert!(!inputs_spirv.is_empty()); // Assemble all the SPIR-V assembly. let mut assembled_spirv = Vec::>::new(); 'spirv: for input in &inputs_spirv.inner { let output = match Command::new("spirv-as") - .arg(&input.filename) + .arg(input.inner.input_path(DIR_IN)) .arg("-o") .arg("-") .output() @@ -210,19 +222,32 @@ fn frontends(c: &mut Criterion) { let total_bytes = assembled_spirv.iter().map(|spv| spv.len() as u64).sum(); + assert!(assembled_spirv.len() == inputs_spirv.inner.len() || assembled_spirv.is_empty()); + group.throughput(Throughput::Bytes(total_bytes)); group.bench_function("shader: spv-in", |b| { b.iter(|| { - let options = naga::front::spv::Options::default(); - for input in &assembled_spirv { - let parser = naga::front::spv::Frontend::new(input.iter().cloned(), &options); + for (i, input) in assembled_spirv.iter().enumerate() { + let params = &inputs_spirv.inner[i].options; + let SpirvInParameters { + adjust_coordinate_space, + } = params.spv_in; + + let parser = naga::front::spv::Frontend::new( + input.iter().cloned(), + &naga::front::spv::Options { + adjust_coordinate_space, + strict_capabilities: true, + ..Default::default() + }, + ); parser.parse().unwrap(); } }); }); - let mut inputs_vertex = Inputs::from_dir("../naga/tests/in/glsl", "vert"); - let mut inputs_fragment = Inputs::from_dir("../naga/tests/in/glsl", "frag"); + let mut inputs_vertex = Inputs::from_dir("glsl", "vert"); + let mut inputs_fragment = Inputs::from_dir("glsl", "frag"); assert!(!inputs_vertex.is_empty()); assert!(!inputs_fragment.is_empty()); // let mut inputs_compute = Inputs::from_dir("../naga/tests/in/glsl", "comp"); @@ -302,14 +327,16 @@ fn backends(c: &mut Criterion) { group.bench_function("shader: wgsl-out", |b| { b.iter(|| { let mut string = String::new(); - let flags = naga::back::wgsl::WriterFlags::empty(); for input in &inputs.inner { - let mut writer = naga::back::wgsl::Writer::new(&mut string, flags); - let _ = writer.write( - input.module.as_ref().unwrap(), - input.module_info.as_ref().unwrap(), - ); - string.clear(); + if input.options.targets.unwrap().contains(Targets::WGSL) { + let mut writer = + naga::back::wgsl::Writer::new(&mut string, (&input.options.wgsl).into()); + let _ = writer.write( + input.module.as_ref().unwrap(), + input.module_info.as_ref().unwrap(), + ); + string.clear(); + } } }); }); @@ -317,21 +344,28 @@ fn backends(c: &mut Criterion) { group.bench_function("shader: spv-out", |b| { b.iter(|| { let mut data = Vec::new(); - let options = naga::back::spv::Options::default(); + let mut writer = naga::back::spv::Writer::new(&Default::default()).unwrap(); for input in &inputs.inner { - if input.filename.contains("pointer-function-arg") { - // These fail due to https://github.com/gfx-rs/wgpu/issues/7315 - continue; + if input.options.targets.unwrap().contains(Targets::SPIRV) { + if input.filename().contains("pointer-function-arg") { + // These fail due to https://github.com/gfx-rs/wgpu/issues/7315 + continue; + } + let opt = input + .options + .spv + .to_options(input.options.bounds_check_policies, None); + if writer.set_options(&opt).is_ok() { + let _ = writer.write( + input.module.as_ref().unwrap(), + input.module_info.as_ref().unwrap(), + None, + &None, + &mut data, + ); + data.clear(); + } } - let mut writer = naga::back::spv::Writer::new(&options).unwrap(); - let _ = writer.write( - input.module.as_ref().unwrap(), - input.module_info.as_ref().unwrap(), - None, - &None, - &mut data, - ); - data.clear(); } }); }); @@ -340,25 +374,27 @@ fn backends(c: &mut Criterion) { let mut data = Vec::new(); let options = naga::back::spv::Options::default(); for input in &inputs.inner { - if input.filename.contains("pointer-function-arg") { - // These fail due to https://github.com/gfx-rs/wgpu/issues/7315 - continue; - } - let mut writer = naga::back::spv::Writer::new(&options).unwrap(); - let module = input.module.as_ref().unwrap(); - for ep in module.entry_points.iter() { - let pipeline_options = naga::back::spv::PipelineOptions { - shader_stage: ep.stage, - entry_point: ep.name.clone(), - }; - let _ = writer.write( - input.module.as_ref().unwrap(), - input.module_info.as_ref().unwrap(), - Some(&pipeline_options), - &None, - &mut data, - ); - data.clear(); + if input.options.targets.unwrap().contains(Targets::SPIRV) { + if input.filename().contains("pointer-function-arg") { + // These fail due to https://github.com/gfx-rs/wgpu/issues/7315 + continue; + } + let mut writer = naga::back::spv::Writer::new(&options).unwrap(); + let module = input.module.as_ref().unwrap(); + for ep in module.entry_points.iter() { + let pipeline_options = naga::back::spv::PipelineOptions { + shader_stage: ep.stage, + entry_point: ep.name.clone(), + }; + let _ = writer.write( + input.module.as_ref().unwrap(), + input.module_info.as_ref().unwrap(), + Some(&pipeline_options), + &None, + &mut data, + ); + data.clear(); + } } } }); @@ -369,15 +405,17 @@ fn backends(c: &mut Criterion) { let mut string = String::new(); let options = naga::back::msl::Options::default(); for input in &inputs.inner { - let pipeline_options = naga::back::msl::PipelineOptions::default(); - let mut writer = naga::back::msl::Writer::new(&mut string); - let _ = writer.write( - input.module.as_ref().unwrap(), - input.module_info.as_ref().unwrap(), - &options, - &pipeline_options, - ); - string.clear(); + if input.options.targets.unwrap().contains(Targets::METAL) { + let pipeline_options = naga::back::msl::PipelineOptions::default(); + let mut writer = naga::back::msl::Writer::new(&mut string); + let _ = writer.write( + input.module.as_ref().unwrap(), + input.module_info.as_ref().unwrap(), + &options, + &pipeline_options, + ); + string.clear(); + } } }); }); @@ -387,15 +425,17 @@ fn backends(c: &mut Criterion) { let options = naga::back::hlsl::Options::default(); let mut string = String::new(); for input in &inputs.inner { - let pipeline_options = Default::default(); - let mut writer = - naga::back::hlsl::Writer::new(&mut string, &options, &pipeline_options); - let _ = writer.write( - input.module.as_ref().unwrap(), - input.module_info.as_ref().unwrap(), - None, - ); // may fail on unimplemented things - string.clear(); + if input.options.targets.unwrap().contains(Targets::HLSL) { + let pipeline_options = Default::default(); + let mut writer = + naga::back::hlsl::Writer::new(&mut string, &options, &pipeline_options); + let _ = writer.write( + input.module.as_ref().unwrap(), + input.module_info.as_ref().unwrap(), + None, + ); // may fail on unimplemented things + string.clear(); + } } }); }); @@ -410,6 +450,9 @@ fn backends(c: &mut Criterion) { zero_initialize_workgroup_memory: true, }; for input in &inputs.inner { + if !input.options.targets.unwrap().contains(Targets::GLSL) { + continue; + } let module = input.module.as_ref().unwrap(); let info = input.module_info.as_ref().unwrap(); for ep in module.entry_points.iter() { diff --git a/clippy.toml b/clippy.toml index 35f212da623..1910b9fcb28 100644 --- a/clippy.toml +++ b/clippy.toml @@ -3,6 +3,4 @@ disallowed-types = [ { path = "std::collections::HashMap", reason = "use hashbrown::HashMap instead" }, { path = "std::collections::HashSet", reason = "use hashbrown::HashSet instead" }, - { path = "rustc_hash::FxHashMap", reason = "use hashbrown::HashMap instead" }, - { path = "rustc_hash::FxHashSet", reason = "use hashbrown::HashSet instead" }, ] diff --git a/cts_runner/Cargo.toml b/cts_runner/Cargo.toml index 6b96ae66c30..689297daf30 100644 --- a/cts_runner/Cargo.toml +++ b/cts_runner/Cargo.toml @@ -15,6 +15,7 @@ env_logger.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] deno_console.workspace = true deno_core.workspace = true +deno_features.workspace = true deno_url.workspace = true deno_web.workspace = true deno_webidl.workspace = true diff --git a/cts_runner/revision.txt b/cts_runner/revision.txt index 495758ec245..a63351a9d1c 100644 --- a/cts_runner/revision.txt +++ b/cts_runner/revision.txt @@ -1 +1 @@ -223e1bfb039319ced939020379f7f489640a87cb +5e7bd6ed86201123ff6b0900974587550afb10e7 diff --git a/cts_runner/src/bootstrap.js b/cts_runner/src/bootstrap.js index 6f3a2dc3a30..220a7201384 100644 --- a/cts_runner/src/bootstrap.js +++ b/cts_runner/src/bootstrap.js @@ -138,10 +138,6 @@ class Navigator { constructor() { webidl.illegalConstructor(); } - - [Symbol.for("Deno.customInspect")](inspect) { - return `${this.constructor.name} ${inspect({})}`; - } } const NavigatorPrototype = Navigator.prototype; @@ -220,8 +216,10 @@ const windowOrWorkerGlobalScope = { GPURenderBundle: util.nonEnumerable(webgpu.GPURenderBundle), GPUQuerySet: util.nonEnumerable(webgpu.GPUQuerySet), GPUError: util.nonEnumerable(webgpu.GPUError), + GPUInternalError: util.nonEnumerable(webgpu.GPUInternalError), GPUValidationError: util.nonEnumerable(webgpu.GPUValidationError), GPUOutOfMemoryError: util.nonEnumerable(webgpu.GPUOutOfMemoryError), + GPUUncapturedErrorEvent: util.nonEnumerable(webgpu.GPUUncapturedErrorEvent), }; windowOrWorkerGlobalScope.console.enumerable = false; diff --git a/cts_runner/src/main.rs b/cts_runner/src/main.rs index 157567fe6e7..cbb88b866f7 100644 --- a/cts_runner/src/main.rs +++ b/cts_runner/src/main.rs @@ -30,23 +30,16 @@ pub async fn run() -> Result<(), AnyError> { .ok_or_else(|| anyhow!("missing specifier in first command line argument"))?; let specifier = resolve_url_or_path(&url, &env::current_dir()?)?; - let mut feature_checker = deno_core::FeatureChecker::default(); - feature_checker.enable_feature(deno_webgpu::UNSTABLE_FEATURE_NAME); - let options = RuntimeOptions { module_loader: Some(Rc::new(deno_core::FsModuleLoader)), extensions: vec![ - deno_webidl::deno_webidl::init_ops_and_esm(), - deno_console::deno_console::init_ops_and_esm(), - deno_url::deno_url::init_ops_and_esm(), - deno_web::deno_web::init_ops_and_esm::( - Arc::new(BlobStore::default()), - None, - ), - deno_webgpu::deno_webgpu::init_ops_and_esm(), - cts_runner::init_ops_and_esm(), + deno_webidl::deno_webidl::init(), + deno_console::deno_console::init(), + deno_url::deno_url::init(), + deno_web::deno_web::init::(Arc::new(BlobStore::default()), None), + deno_webgpu::deno_webgpu::init(), + cts_runner::init(), ], - feature_checker: Some(Arc::new(feature_checker)), ..Default::default() }; let mut js_runtime = JsRuntime::new(options); @@ -84,6 +77,9 @@ deno_core::extension!( esm_entry_point = "ext:cts_runner/src/bootstrap.js", esm = ["src/bootstrap.js"], state = |state| { + let mut feature_checker = deno_features::FeatureChecker::default(); + feature_checker.enable_feature(deno_webgpu::UNSTABLE_FEATURE_NAME); + state.put(feature_checker); state.put(Permissions {}); } ); diff --git a/cts_runner/test.lst b/cts_runner/test.lst index 163944db9ad..4118604fc53 100644 --- a/cts_runner/test.lst +++ b/cts_runner/test.lst @@ -1,10 +1,40 @@ +// The following may be useful to generate lists of tests for this file: +// ``` +// cargo xtask cts -- --list +// ``` unittests:* +webgpu:api,operation,buffers,createBindGroup:buffer_binding_resource:* webgpu:api,operation,command_buffer,basic:* webgpu:api,operation,command_buffer,copyBufferToBuffer:* +fails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth24plus" +fails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth24plus-stencil8" +fails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth16unorm" +fails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth32float" +fails-if(vulkan) webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="depth32float-stencil8" +webgpu:api,operation,command_buffer,copyTextureToTexture:copy_depth_stencil:format="stencil8" +// Fails with OOM in CI. +fails-if(dx12) webgpu:api,operation,command_buffer,image_copy:offsets_and_sizes:* +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="WriteTexture";checkMethod="FullCopyT2B";dimension="1d" +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="WriteTexture";checkMethod="FullCopyT2B";dimension="2d" +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="WriteTexture";checkMethod="FullCopyT2B";dimension="3d" +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="WriteTexture";checkMethod="PartialCopyT2B";dimension="1d" +fails-if(dx12) webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="WriteTexture";checkMethod="PartialCopyT2B";dimension="2d" +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="WriteTexture";checkMethod="PartialCopyT2B";dimension="3d" +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="CopyB2T";checkMethod="FullCopyT2B";dimension="1d" +webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="CopyB2T";checkMethod="FullCopyT2B";dimension="2d" +fails-if(dx12,vulkan,metal) webgpu:api,operation,command_buffer,image_copy:undefined_params:initMethod="CopyB2T";checkMethod="FullCopyT2B";dimension="3d" webgpu:api,operation,compute,basic:memcpy:* //FAIL: webgpu:api,operation,compute,basic:large_dispatch:* webgpu:api,operation,compute_pipeline,overrides:* webgpu:api,operation,device,lost:* +webgpu:api,operation,render_pass,storeOp:* +fails-if(vulkan) webgpu:api,operation,vertex_state,correctness:array_stride_zero:* +// Presumably vertex pulling, revisit after https://github.com/gfx-rs/wgpu/issues/7981 is fixed. +fails-if(metal) webgpu:api,operation,vertex_state,correctness:setVertexBuffer_offset_and_attribute_offset:* +fails-if(dx12) webgpu:api,validation,capability_checks,limits,maxBindGroups:setBindGroup,* +webgpu:api,validation,createBindGroup:buffer,effective_buffer_binding_size:* +// Fails because we coerce a size of 0 in `GPUDevice.createBindGroup(…)` to `buffer.size - offset`. +// FAIL webgpu:api,validation,createBindGroup:buffer_offset_and_size_for_bind_groups_match:* webgpu:api,validation,encoding,beginComputePass:* webgpu:api,validation,encoding,beginRenderPass:* webgpu:api,validation,encoding,cmds,clearBuffer:* @@ -15,18 +45,28 @@ webgpu:api,validation,encoding,cmds,copyTextureToTexture:texture,device_mismatch webgpu:api,validation,encoding,cmds,copyTextureToTexture:mipmap_level:* webgpu:api,validation,encoding,cmds,copyTextureToTexture:texture_usage:* webgpu:api,validation,encoding,cmds,copyTextureToTexture:sample_count:* -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:multisampled_copy_restrictions:* -// texture_format_compatibility fails on Windows and Mac in CI. -// It passes locally on Mac. It is also a relatively long test. -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:texture_format_compatibility:* -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:depth_stencil_copy_restrictions:* -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges:* -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_within_same_texture:* -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_aspects:* -// Fails on Windows, https://github.com/gfx-rs/wgpu/issues/7844. -//FAIL: webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges_with_compressed_texture_formats:* +webgpu:api,validation,encoding,cmds,copyTextureToTexture:multisampled_copy_restrictions:* +webgpu:api,validation,encoding,cmds,copyTextureToTexture:texture_format_compatibility:* +webgpu:api,validation,encoding,cmds,copyTextureToTexture:depth_stencil_copy_restrictions:* +// Intermittently fails on dx12, https://github.com/gfx-rs/wgpu/issues/8118 +fails-if(dx12) webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges:* +webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_within_same_texture:* +webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_aspects:* +webgpu:api,validation,encoding,cmds,copyTextureToTexture:copy_ranges_with_compressed_texture_formats:* +webgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType="non-pass" +webgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType="compute%20pass" +webgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType="render%20pass" +//FAIL: webgpu:api,validation,encoding,cmds,debug:debug_group_balanced:encoderType="render%20bundle" +// https://github.com/gfx-rs/wgpu/issues/8039 +webgpu:api,validation,encoding,cmds,debug:debug_group:* +webgpu:api,validation,encoding,cmds,debug:debug_marker:* webgpu:api,validation,encoding,cmds,index_access:* //FAIL: webgpu:api,validation,encoding,cmds,render,draw:* +webgpu:api,validation,encoding,cmds,render,draw:index_buffer_OOB:* +webgpu:api,validation,encoding,cmds,render,draw:unused_buffer_bound:* +webgpu:api,validation,encoding,cmds,render,dynamic_state:* +webgpu:api,validation,encoding,cmds,render,setIndexBuffer:* +webgpu:api,validation,encoding,cmds,render,setVertexBuffer:* webgpu:api,validation,encoding,encoder_state:* webgpu:api,validation,encoding,encoder_open_state:non_pass_commands:* webgpu:api,validation,encoding,encoder_open_state:render_pass_commands:* @@ -43,15 +83,48 @@ webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:bind_grou webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:bind_groups_and_pipeline_layout_mismatch:encoderType="render%20pass";* webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:buffer_binding,render_pipeline:* webgpu:api,validation,encoding,programmable,pipeline_bind_group_compat:sampler_binding,render_pipeline:* +webgpu:api,validation,encoding,queries,general:occlusion_query,query_index:* +webgpu:shader,validation,extension,dual_source_blending:blend_src_syntax_validation:* +webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_usage_and_aspect:* +webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_size:* +// image_copy depth/stencil failures on dx12: https://github.com/gfx-rs/wgpu/issues/8133 +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="stencil8";aspect="stencil-only";copyType="CopyB2T" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="stencil8";aspect="stencil-only";copyType="CopyT2B" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth32float";aspect="depth-only";copyType="CopyT2B" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth24plus-stencil8";aspect="stencil-only";copyType="CopyB2T" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:depth_stencil_format,copy_buffer_offset:format="depth24plus-stencil8";aspect="stencil-only";copyType="CopyT2B" +//mix of PASS and FAIL: other subtests of copy_buffer_offset. Related bugs: +// https://github.com/gfx-rs/wgpu/issues/7946 +webgpu:api,validation,image_copy,buffer_texture_copies:sample_count:* +webgpu:api,validation,image_copy,buffer_texture_copies:texture_buffer_usages:* +webgpu:api,validation,image_copy,buffer_texture_copies:device_mismatch:* +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="r8uint";copyType="CopyB2T";dimension="1d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="r8uint";copyType="CopyT2B";dimension="1d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba32float";copyType="CopyB2T";dimension="1d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba32float";copyType="CopyT2B";dimension="1d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyB2T";dimension="2d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyT2B";dimension="2d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyB2T";dimension="3d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="rgba8unorm";copyType="CopyT2B";dimension="3d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="bgra8unorm";copyType="CopyB2T";dimension="2d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="bgra8unorm";copyType="CopyT2B";dimension="2d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyT2B";dimension="2d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyB2T";dimension="2d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyT2B";dimension="3d" +fails-if(dx12) webgpu:api,validation,image_copy,buffer_texture_copies:offset_and_bytesPerRow:format="astc-4x4-unorm";copyType="CopyB2T";dimension="3d" +//mix of PASS and FAIL: other subtests of offset_and_bytesPerRow. Related bugs: +// https://github.com/gfx-rs/wgpu/issues/7946 webgpu:api,validation,image_copy,layout_related:copy_end_overflows_u64:* +// Fails with OOM in CI. +fails-if(dx12) webgpu:api,validation,image_copy,layout_related:offset_alignment:* webgpu:api,validation,image_copy,texture_related:format:dimension="1d";* -webgpu:api,validation,queue,submit:command_buffer,device_mismatch:* -webgpu:api,validation,queue,submit:command_buffer,duplicate_buffers:* -webgpu:api,validation,queue,submit:command_buffer,submit_invalidates:* -//FAIL: webgpu:api,validation,queue,submit:command_buffer,invalid_submit_invalidates:* -// https://github.com/gfx-rs/wgpu/issues/3911#issuecomment-2972995675 +webgpu:api,validation,queue,submit:command_buffer,* webgpu:api,validation,render_pass,render_pass_descriptor:attachments,* +webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,depthSlice,bound_check:* +// Fails due to missing validation. +// FAIL: webgpu:api,validation,render_pass,render_pass_descriptor:color_attachments,depthSlice,overlaps,diff_miplevel:* webgpu:api,validation,render_pass,render_pass_descriptor:resolveTarget,* +webgpu:api,validation,render_pass,resolve:resolve_attachment:* webgpu:api,validation,texture,rg11b10ufloat_renderable:* webgpu:api,operation,render_pipeline,overrides:* webgpu:api,operation,rendering,basic:clear:* @@ -63,16 +136,23 @@ webgpu:api,operation,rendering,color_target_state:blend_constant,setting:* webgpu:api,operation,rendering,depth:* webgpu:api,operation,rendering,draw:* webgpu:api,operation,shader_module,compilation_info:* -webgpu:api,operation,uncapturederror:iff_uncaptured:* +webgpu:api,operation,vertex_state,correctness:non_zero_array_stride_and_attribute_offset:* +// Likely due to https://github.com/gfx-rs/wgpu/issues/7357. +fails-if(metal) webgpu:api,operation,uncapturederror:iff_uncaptured:* //FAIL: webgpu:shader,execution,expression,call,builtin,select:* // - Fails with `const`/abstract int cases on all platforms because of . // - Fails with `vec3` & `f16` cases on macOS because of . //FAIL: webgpu:api,operation,uncapturederror:onuncapturederror_order_wrt_addEventListener // There are also two unimplemented SKIPs in uncapturederror not enumerated here. webgpu:api,validation,encoding,queries,general:occlusion_query,query_type:* +webgpu:shader,execution,expression,call,builtin,atan2:f16:* +webgpu:shader,execution,expression,call,builtin,atan2:f32:* webgpu:shader,execution,expression,call,builtin,textureSample:sampled_1d_coords:* webgpu:shader,execution,expression,call,builtin,textureSampleBaseClampToEdge:2d_coords:stage="c";textureType="texture_2d";* webgpu:shader,execution,flow_control,return:* +// Many other vertex_buffer_access subtests also passing, but there are too many to enumerate. +// Fails on Metal in CI only, not when running locally. +fails-if(metal) webgpu:shader,execution,robust_access_vertex:vertex_buffer_access:indexed=true;indirect=false;drawCallTestParameter="baseVertex";type="float32x4";additionalBuffers=4;partialLastNumber=false;offsetVertexBuffer=true webgpu:shader,validation,expression,call,builtin,max:values:* webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="break" webgpu:shader,validation,statement,statement_behavior:invalid_statements:body="break_if" diff --git a/cts_runner/tests/integration.rs b/cts_runner/tests/integration.rs index 199f00e6315..bb2118b078c 100644 --- a/cts_runner/tests/integration.rs +++ b/cts_runner/tests/integration.rs @@ -34,7 +34,7 @@ impl Display for JsError { impl Debug for JsError { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", self) + write!(f, "{self}") } } diff --git a/deno_webgpu/01_webgpu.js b/deno_webgpu/01_webgpu.js index 5345a2e624f..8c5ce5c5933 100644 --- a/deno_webgpu/01_webgpu.js +++ b/deno_webgpu/01_webgpu.js @@ -37,6 +37,8 @@ import { GPUTextureView, GPUExternalTexture, op_create_gpu, + op_webgpu_device_start_capture, + op_webgpu_device_stop_capture, } from "ext:core/ops"; const { ObjectDefineProperty, @@ -57,9 +59,10 @@ import { import { DOMException } from "ext:deno_web/01_dom_exception.js"; import { createFilteredInspectProxy } from "ext:deno_console/01_console.js"; -const customInspect = SymbolFor("Deno.privateCustomInspect"); +const privateCustomInspect = SymbolFor("Deno.privateCustomInspect"); const _message = Symbol("[[message]]"); const illegalConstructorKey = Symbol("illegalConstructorKey"); + class GPUError { constructor(key = null) { if (key !== illegalConstructorKey) { @@ -73,7 +76,7 @@ class GPUError { return this[_message]; } - [customInspect](inspect, inspectOptions) { + [privateCustomInspect](inspect, inspectOptions) { return inspect( createFilteredInspectProxy({ object: this, @@ -170,14 +173,16 @@ class GPUUncapturedErrorEvent extends Event { } const GPUUncapturedErrorEventPrototype = GPUUncapturedErrorEvent.prototype; -ObjectDefineProperty(GPU, customInspect, { +const GPUPrototype = GPU.prototype; +ObjectDefineProperty(GPUPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return `${this.constructor.name} ${inspect({}, inspectOptions)}`; }, }); -ObjectDefineProperty(GPUAdapter, customInspect, { +const GPUAdapterPrototype = GPUAdapter.prototype; +ObjectDefineProperty(GPUAdapterPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -188,16 +193,15 @@ ObjectDefineProperty(GPUAdapter, customInspect, { "features", "limits", "info", - "isFallbackAdapter", ], }), inspectOptions, ); }, }); -const GPUAdapterPrototype = GPUAdapter.prototype; -ObjectDefineProperty(GPUAdapterInfo, customInspect, { +const GPUAdapterInfoPrototype = GPUAdapterInfo.prototype; +ObjectDefineProperty(GPUAdapterInfoPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -209,15 +213,19 @@ ObjectDefineProperty(GPUAdapterInfo, customInspect, { "architecture", "device", "description", + "subgroupMinSize", + "subgroupMaxSize", + "isFallbackAdapter", ], }), inspectOptions, ); }, }); -const GPUAdapterInfoPrototype = GPUAdapterInfo.prototype; -ObjectDefineProperty(GPUSupportedFeatures, customInspect, { +const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype; +webidl.setlikeObjectWrap(GPUSupportedFeaturesPrototype, true); +ObjectDefineProperty(GPUSupportedFeaturesPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { if (ObjectPrototypeIsPrototypeOf(GPUSupportedFeaturesPrototype, this)) { @@ -229,10 +237,61 @@ ObjectDefineProperty(GPUSupportedFeatures, customInspect, { } }, }); -const GPUSupportedFeaturesPrototype = GPUSupportedFeatures.prototype; -webidl.setlikeObjectWrap(GPUSupportedFeaturesPrototype, true); -ObjectDefineProperty(GPUDeviceLostInfo, customInspect, { +const GPUSupportedLimitsPrototype = GPUSupportedLimits.prototype; +ObjectDefineProperty(GPUSupportedLimitsPrototype, privateCustomInspect, { + __proto__: null, + value(inspect, inspectOptions) { + return inspect( + createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + GPUSupportedLimitsPrototype, + this, + ), + keys: [ + "maxTextureDimension1D", + "maxTextureDimension2D", + "maxTextureDimension3D", + "maxTextureArrayLayers", + "maxBindGroups", + // TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers + // "maxBindGroupsPlusVertexBuffers", + "maxBindingsPerBindGroup", + "maxDynamicUniformBuffersPerPipelineLayout", + "maxDynamicStorageBuffersPerPipelineLayout", + "maxSampledTexturesPerShaderStage", + "maxSamplersPerShaderStage", + "maxStorageBuffersPerShaderStage", + "maxStorageTexturesPerShaderStage", + "maxUniformBuffersPerShaderStage", + "maxUniformBufferBindingSize", + "maxStorageBufferBindingSize", + "minUniformBufferOffsetAlignment", + "minStorageBufferOffsetAlignment", + "maxVertexBuffers", + "maxBufferSize", + "maxVertexAttributes", + "maxVertexBufferArrayStride", + // TODO(@crowlKats): support max_inter_stage_shader_variables + // "maxInterStageShaderVariables", + "maxColorAttachments", + "maxColorAttachmentBytesPerSample", + "maxComputeWorkgroupStorageSize", + "maxComputeInvocationsPerWorkgroup", + "maxComputeWorkgroupSizeX", + "maxComputeWorkgroupSizeY", + "maxComputeWorkgroupSizeZ", + "maxComputeWorkgroupsPerDimension", + ], + }), + inspectOptions, + ); + }, +}); + +const GPUDeviceLostInfoPrototype = GPUDeviceLostInfo.prototype; +ObjectDefineProperty(GPUDeviceLostInfoPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -251,9 +310,11 @@ ObjectDefineProperty(GPUDeviceLostInfo, customInspect, { ); }, }); -const GPUDeviceLostInfoPrototype = GPUDeviceLostInfo.prototype; -ObjectDefineProperty(GPUDevice, customInspect, { +const GPUDevicePrototype = GPUDevice.prototype; +ObjectSetPrototypeOf(GPUDevicePrototype, EventTargetPrototype); +defineEventHandler(GPUDevicePrototype, "uncapturederror"); +ObjectDefineProperty(GPUDevicePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -274,11 +335,9 @@ ObjectDefineProperty(GPUDevice, customInspect, { ); }, }); -const GPUDevicePrototype = GPUDevice.prototype; -ObjectSetPrototypeOf(GPUDevicePrototype, EventTargetPrototype); -defineEventHandler(GPUDevice.prototype, "uncapturederror"); -ObjectDefineProperty(GPUQueue, customInspect, { +const GPUQueuePrototype = GPUQueue.prototype; +ObjectDefineProperty(GPUQueuePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -293,9 +352,9 @@ ObjectDefineProperty(GPUQueue, customInspect, { ); }, }); -const GPUQueuePrototype = GPUQueue.prototype; -ObjectDefineProperty(GPUBuffer, customInspect, { +const GPUBufferPrototype = GPUBuffer.prototype; +ObjectDefineProperty(GPUBufferPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -313,7 +372,6 @@ ObjectDefineProperty(GPUBuffer, customInspect, { ); }, }); -const GPUBufferPrototype = GPUBuffer.prototype; class GPUBufferUsage { constructor() { @@ -365,7 +423,8 @@ class GPUMapMode { } } -ObjectDefineProperty(GPUTexture, customInspect, { +const GPUTexturePrototype = GPUTexture.prototype; +ObjectDefineProperty(GPUTexturePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -388,7 +447,6 @@ ObjectDefineProperty(GPUTexture, customInspect, { ); }, }); -const GPUTexturePrototype = GPUTexture.prototype; class GPUTextureUsage { constructor() { @@ -412,7 +470,8 @@ class GPUTextureUsage { } } -ObjectDefineProperty(GPUTextureView, customInspect, { +const GPUTextureViewPrototype = GPUTextureView.prototype; +ObjectDefineProperty(GPUTextureViewPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -427,20 +486,29 @@ ObjectDefineProperty(GPUTextureView, customInspect, { ); }, }); -const GPUTextureViewPrototype = GPUTextureView.prototype; -ObjectDefineProperty(GPUSampler, customInspect, { +const GPUSamplerPrototype = GPUSampler.prototype; +ObjectDefineProperty(GPUSamplerPrototype, privateCustomInspect, { __proto__: null, - value(inspect) { - return `${this.constructor.name} ${ - inspect({ - label: this.label, - }) - }`; + value(inspect, inspectOptions) { + return inspect( + createFilteredInspectProxy({ + object: this, + evaluate: ObjectPrototypeIsPrototypeOf( + GPUSamplerPrototype, + this, + ), + keys: [ + "label", + ], + }), + inspectOptions, + ); }, }); -ObjectDefineProperty(GPUBindGroupLayout, customInspect, { +const GPUBindGroupLayoutPrototype = GPUBindGroupLayout.prototype; +ObjectDefineProperty(GPUBindGroupLayout, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -458,9 +526,9 @@ ObjectDefineProperty(GPUBindGroupLayout, customInspect, { ); }, }); -const GPUBindGroupLayoutPrototype = GPUBindGroupLayout.prototype; -ObjectDefineProperty(GPUPipelineLayout, customInspect, { +const GPUPipelineLayoutPrototype = GPUPipelineLayout.prototype; +ObjectDefineProperty(GPUPipelineLayoutPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -478,9 +546,9 @@ ObjectDefineProperty(GPUPipelineLayout, customInspect, { ); }, }); -const GPUPipelineLayoutPrototype = GPUPipelineLayout.prototype; -ObjectDefineProperty(GPUBindGroup, customInspect, { +const GPUBindGroupPrototype = GPUBindGroup.prototype; +ObjectDefineProperty(GPUBindGroupPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -495,9 +563,9 @@ ObjectDefineProperty(GPUBindGroup, customInspect, { ); }, }); -const GPUBindGroupPrototype = GPUBindGroup.prototype; -ObjectDefineProperty(GPUShaderModule, customInspect, { +const GPUShaderModulePrototype = GPUShaderModule.prototype; +ObjectDefineProperty(GPUShaderModulePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -512,9 +580,8 @@ ObjectDefineProperty(GPUShaderModule, customInspect, { ); }, }); -const GPUShaderModulePrototype = GPUShaderModule.prototype; -ObjectDefineProperty(GPUCompilationInfo, customInspect, { +ObjectDefineProperty(GPUCompilationInfo, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -534,7 +601,7 @@ ObjectDefineProperty(GPUCompilationInfo, customInspect, { }); const GPUCompilationInfoPrototype = GPUCompilationInfo.prototype; -ObjectDefineProperty(GPUCompilationMessage, customInspect, { +ObjectDefineProperty(GPUCompilationMessage, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -558,13 +625,6 @@ ObjectDefineProperty(GPUCompilationMessage, customInspect, { }, }); const GPUCompilationMessagePrototype = GPUCompilationMessage.prototype; -// Naming it `type` or `r#type` in Rust does not work. -// https://github.com/gfx-rs/wgpu/issues/7778 -ObjectDefineProperty(GPUCompilationMessage.prototype, "type", { - get() { - return this.ty; - } -}); class GPUShaderStage { constructor() { @@ -584,7 +644,8 @@ class GPUShaderStage { } } -ObjectDefineProperty(GPUComputePipeline, customInspect, { +const GPUComputePipelinePrototype = GPUComputePipeline.prototype; +ObjectDefineProperty(GPUComputePipelinePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -602,9 +663,9 @@ ObjectDefineProperty(GPUComputePipeline, customInspect, { ); }, }); -const GPUComputePipelinePrototype = GPUComputePipeline.prototype; -ObjectDefineProperty(GPURenderPipeline, customInspect, { +const GPURenderPipelinePrototype = GPURenderPipeline.prototype; +ObjectDefineProperty(GPURenderPipelinePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -622,7 +683,6 @@ ObjectDefineProperty(GPURenderPipeline, customInspect, { ); }, }); -const GPURenderPipelinePrototype = GPURenderPipeline.prototype; class GPUColorWrite { constructor() { @@ -646,7 +706,8 @@ class GPUColorWrite { } } -ObjectDefineProperty(GPUCommandEncoder, customInspect, { +const GPUCommandEncoderPrototype = GPUCommandEncoder.prototype; +ObjectDefineProperty(GPUCommandEncoderPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -664,9 +725,9 @@ ObjectDefineProperty(GPUCommandEncoder, customInspect, { ); }, }); -const GPUCommandEncoderPrototype = GPUCommandEncoder.prototype; -ObjectDefineProperty(GPURenderPassEncoder, customInspect, { +const GPURenderPassEncoderPrototype = GPURenderPassEncoder.prototype; +ObjectDefineProperty(GPURenderPassEncoderPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -684,9 +745,9 @@ ObjectDefineProperty(GPURenderPassEncoder, customInspect, { ); }, }); -const GPURenderPassEncoderPrototype = GPURenderPassEncoder.prototype; -ObjectDefineProperty(GPUComputePassEncoder, customInspect, { +const GPUComputePassEncoderPrototype = GPUComputePassEncoder.prototype; +ObjectDefineProperty(GPUComputePassEncoderPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -704,9 +765,9 @@ ObjectDefineProperty(GPUComputePassEncoder, customInspect, { ); }, }); -const GPUComputePassEncoderPrototype = GPUComputePassEncoder.prototype; -ObjectDefineProperty(GPUCommandBuffer, customInspect, { +const GPUCommandBufferPrototype = GPUCommandBuffer.prototype; +ObjectDefineProperty(GPUCommandBufferPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -721,9 +782,9 @@ ObjectDefineProperty(GPUCommandBuffer, customInspect, { ); }, }); -const GPUCommandBufferPrototype = GPUCommandBuffer.prototype; -ObjectDefineProperty(GPURenderBundleEncoder, customInspect, { +const GPURenderBundleEncoderPrototype = GPURenderBundleEncoder.prototype; +ObjectDefineProperty(GPURenderBundleEncoderPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -741,9 +802,9 @@ ObjectDefineProperty(GPURenderBundleEncoder, customInspect, { ); }, }); -const GPURenderBundleEncoderPrototype = GPURenderBundleEncoder.prototype; -ObjectDefineProperty(GPURenderBundle, customInspect, { +const GPURenderBundlePrototype = GPURenderBundle.prototype; +ObjectDefineProperty(GPURenderBundlePrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -758,9 +819,9 @@ ObjectDefineProperty(GPURenderBundle, customInspect, { ); }, }); -const GPURenderBundlePrototype = GPURenderBundle.prototype; -ObjectDefineProperty(GPUQuerySet, customInspect, { +const GPUQuerySetPrototype = GPUQuerySet.prototype; +ObjectDefineProperty(GPUQuerySetPrototype, privateCustomInspect, { __proto__: null, value(inspect, inspectOptions) { return inspect( @@ -777,14 +838,6 @@ ObjectDefineProperty(GPUQuerySet, customInspect, { ); }, }); -const GPUQuerySetPrototype = GPUQuerySet.prototype; -// Naming it `type` or `r#type` in Rust does not work. -// https://github.com/gfx-rs/wgpu/issues/7778 -ObjectDefineProperty(GPUQuerySet.prototype, "type", { - get() { - return this.ty; - } -}); // Converters @@ -819,6 +872,19 @@ webidl.converters["GPUUncapturedErrorEventInit"] = webidl dictMembersGPUUncapturedErrorEventInit, ); +function deviceStartCapture(device) { + op_webgpu_device_start_capture(device); +} + +function deviceStopCapture(device) { + op_webgpu_device_stop_capture(device); +} + +const denoNsWebGPU = { + deviceStartCapture, + deviceStopCapture, +}; + let gpu; function initGPU() { if (!gpu) { @@ -831,6 +897,7 @@ function initGPU() { } export { + denoNsWebGPU, GPU, gpu, GPUAdapter, diff --git a/deno_webgpu/Cargo.toml b/deno_webgpu/Cargo.toml index 08545ef8d2a..52df54ec76d 100644 --- a/deno_webgpu/Cargo.toml +++ b/deno_webgpu/Cargo.toml @@ -2,7 +2,7 @@ [package] name = "deno_webgpu" -version = "0.157.0" +version = "0.181.0" authors = ["the Deno authors"] edition.workspace = true license = "MIT" diff --git a/deno_webgpu/adapter.rs b/deno_webgpu/adapter.rs index d5916fb0677..711a7190c7b 100644 --- a/deno_webgpu/adapter.rs +++ b/deno_webgpu/adapter.rs @@ -13,6 +13,7 @@ use deno_core::V8TaskSpawner; use deno_core::WebIDL; use super::device::GPUDevice; +use crate::error::GPUGenericError; use crate::webidl::features_to_feature_names; use crate::webidl::GPUFeatureName; use crate::Instance; @@ -20,412 +21,469 @@ use crate::Instance; #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURequestAdapterOptions { - pub power_preference: Option, - #[webidl(default = false)] - pub force_fallback_adapter: bool, + pub power_preference: Option, + #[webidl(default = false)] + pub force_fallback_adapter: bool, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUPowerPreference { - LowPower, - HighPerformance, + LowPower, + HighPerformance, } #[derive(WebIDL)] #[webidl(dictionary)] struct GPUDeviceDescriptor { - #[webidl(default = String::new())] - label: String, - - #[webidl(default = vec![])] - required_features: Vec, - #[webidl(default = Default::default())] - #[options(enforce_range = true)] - required_limits: indexmap::IndexMap>, + #[webidl(default = String::new())] + label: String, + + #[webidl(default = vec![])] + required_features: Vec, + #[webidl(default = Default::default())] + #[options(enforce_range = true)] + required_limits: indexmap::IndexMap>, } pub struct GPUAdapter { - pub instance: Instance, - pub id: wgpu_core::id::AdapterId, + pub instance: Instance, + pub id: wgpu_core::id::AdapterId, - pub features: SameObject, - pub limits: SameObject, - pub info: Rc>, + pub features: SameObject, + pub limits: SameObject, + pub info: Rc>, } impl Drop for GPUAdapter { - fn drop(&mut self) { - self.instance.adapter_drop(self.id); - } + fn drop(&mut self) { + self.instance.adapter_drop(self.id); + } } -impl GarbageCollected for GPUAdapter {} +impl GarbageCollected for GPUAdapter { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUAdapter" + } +} #[op2] impl GPUAdapter { - #[getter] - #[global] - fn info(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.info.get(scope, |_| { - let info = self.instance.adapter_get_info(self.id); - let limits = self.instance.adapter_limits(self.id); - - GPUAdapterInfo { - info, - subgroup_min_size: limits.min_subgroup_size, - subgroup_max_size: limits.max_subgroup_size, - } - }) - } - - #[getter] - #[global] - fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.features.get(scope, |scope| { - let features = self.instance.adapter_features(self.id); - // Only expose WebGPU features, not wgpu native-only features - let features = features & wgpu_types::Features::all_webgpu_mask(); - let features = features_to_feature_names(features); - GPUSupportedFeatures::new(scope, features) - }) - } - #[getter] - #[global] - fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.limits.get(scope, |_| { - let adapter_limits = self.instance.adapter_limits(self.id); - GPUSupportedLimits(adapter_limits) - }) - } - #[getter] - fn is_fallback_adapter(&self) -> bool { - // TODO(lucacasonato): report correctly from wgpu - false - } - - #[async_method(fake)] - #[global] - fn request_device( - &self, - state: &mut OpState, - scope: &mut v8::HandleScope, - #[webidl] descriptor: GPUDeviceDescriptor, - ) -> Result, CreateDeviceError> { - let features = self.instance.adapter_features(self.id); - let supported_features = features_to_feature_names(features); - #[allow(clippy::disallowed_types)] - let required_features = descriptor - .required_features - .iter() - .cloned() - .collect::>(); - - if !required_features.is_subset(&supported_features) { - return Err(CreateDeviceError::RequiredFeaturesNotASubset); - } - - let required_limits = - serde_json::from_value(serde_json::to_value(descriptor.required_limits)?)?; - - let trace = std::env::var_os("DENO_WEBGPU_TRACE") - .map(|path| wgpu_types::Trace::Directory(std::path::PathBuf::from(path))) - .unwrap_or_default(); - - let wgpu_descriptor = wgpu_types::DeviceDescriptor { - label: crate::transform_label(descriptor.label.clone()), - required_features: super::webidl::feature_names_to_features( - descriptor.required_features, - ), - required_limits, - memory_hints: Default::default(), - trace, - }; - - let (device, queue) = - self.instance - .adapter_request_device(self.id, &wgpu_descriptor, None, None)?; - - let spawner = state.borrow::().clone(); - let lost_resolver = v8::PromiseResolver::new(scope).unwrap(); - let lost_promise = lost_resolver.get_promise(scope); - let device = GPUDevice { - instance: self.instance.clone(), - id: device, - queue, - label: descriptor.label, - queue_obj: SameObject::new(), - adapter_info: self.info.clone(), - error_handler: Rc::new(super::error::DeviceErrorHandler::new( - v8::Global::new(scope, lost_resolver), - spawner, - )), - adapter: self.id, - lost_promise: v8::Global::new(scope, lost_promise), - limits: SameObject::new(), - features: SameObject::new(), - }; - let device = deno_core::cppgc::make_cppgc_object(scope, device); - let weak_device = v8::Weak::new(scope, device); - let event_target_setup = state.borrow::(); - let webidl_brand = v8::Local::new(scope, event_target_setup.brand.clone()); - device.set(scope, webidl_brand, webidl_brand); - let set_event_target_data = - v8::Local::new(scope, event_target_setup.set_event_target_data.clone()) - .cast::(); - let null = v8::null(scope); - set_event_target_data.call(scope, null.into(), &[device.into()]); - - // Now that the device is fully constructed, give the error handler a - // weak reference to it. - let device = device.cast::(); - deno_core::cppgc::try_unwrap_cppgc_object::(scope, device) - .unwrap() - .error_handler - .set_device(weak_device); - - Ok(v8::Global::new(scope, device)) - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[global] + fn info(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.info.get(scope, |_| { + let info = self.instance.adapter_get_info(self.id); + let limits = self.instance.adapter_limits(self.id); + + GPUAdapterInfo { + info, + subgroup_min_size: limits.min_subgroup_size, + subgroup_max_size: limits.max_subgroup_size, + } + }) + } + + #[getter] + #[global] + fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.features.get(scope, |scope| { + let features = self.instance.adapter_features(self.id); + // Only expose WebGPU features, not wgpu native-only features + let features = features & wgpu_types::Features::all_webgpu_mask(); + let features = features_to_feature_names(features); + GPUSupportedFeatures::new(scope, features) + }) + } + + #[getter] + #[global] + fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.limits.get(scope, |_| { + let adapter_limits = self.instance.adapter_limits(self.id); + GPUSupportedLimits(adapter_limits) + }) + } + + #[async_method(fake)] + #[global] + fn request_device( + &self, + state: &mut OpState, + scope: &mut v8::HandleScope, + #[webidl] descriptor: GPUDeviceDescriptor, + ) -> Result, CreateDeviceError> { + let features = self.instance.adapter_features(self.id); + let supported_features = features_to_feature_names(features); + #[allow(clippy::disallowed_types)] + let required_features = descriptor + .required_features + .iter() + .cloned() + .collect::>(); + + if !required_features.is_subset(&supported_features) { + return Err(CreateDeviceError::RequiredFeaturesNotASubset); + } + + // When support for compatibility mode is added, this will need to look + // at whether the adapter is "compatibility-defaulting" or + // "core-defaulting", and choose the appropriate set of defaults. + // + // Support for compatibility mode is tracked in + // https://github.com/gfx-rs/wgpu/issues/8124. + let required_limits = serde_json::from_value::( + serde_json::to_value(descriptor.required_limits)?, + )? + .or_better_values_from(&wgpu_types::Limits::default()); + + let trace = std::env::var_os("DENO_WEBGPU_TRACE") + .map(|path| wgpu_types::Trace::Directory(std::path::PathBuf::from(path))) + .unwrap_or_default(); + + let wgpu_descriptor = wgpu_types::DeviceDescriptor { + label: crate::transform_label(descriptor.label.clone()), + required_features: super::webidl::feature_names_to_features( + descriptor.required_features, + ), + required_limits, + experimental_features: wgpu_types::ExperimentalFeatures::disabled(), + memory_hints: Default::default(), + trace, + }; + + let (device, queue) = self.instance.adapter_request_device( + self.id, + &wgpu_descriptor, + None, + None, + )?; + + let spawner = state.borrow::().clone(); + let lost_resolver = v8::PromiseResolver::new(scope).unwrap(); + let lost_promise = lost_resolver.get_promise(scope); + let device = GPUDevice { + instance: self.instance.clone(), + id: device, + queue, + label: descriptor.label, + queue_obj: SameObject::new(), + adapter_info: self.info.clone(), + error_handler: Rc::new(super::error::DeviceErrorHandler::new( + v8::Global::new(scope, lost_resolver), + spawner, + )), + adapter: self.id, + lost_promise: v8::Global::new(scope, lost_promise), + limits: SameObject::new(), + features: SameObject::new(), + }; + let device = deno_core::cppgc::make_cppgc_object(scope, device); + let weak_device = v8::Weak::new(scope, device); + let event_target_setup = state.borrow::(); + let webidl_brand = v8::Local::new(scope, event_target_setup.brand.clone()); + device.set(scope, webidl_brand, webidl_brand); + let set_event_target_data = + v8::Local::new(scope, event_target_setup.set_event_target_data.clone()) + .cast::(); + let null = v8::null(scope); + set_event_target_data.call(scope, null.into(), &[device.into()]); + + // Now that the device is fully constructed, give the error handler a + // weak reference to it. + let device = device.cast::(); + deno_core::cppgc::try_unwrap_cppgc_object::(scope, device) + .unwrap() + .error_handler + .set_device(weak_device); + + Ok(v8::Global::new(scope, device)) + } } #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum CreateDeviceError { - #[class(type)] - #[error("requiredFeatures must be a subset of the adapter features")] - RequiredFeaturesNotASubset, - #[class(inherit)] - #[error(transparent)] - Serde(#[from] serde_json::Error), - #[class(type)] - #[error(transparent)] - Device(#[from] wgpu_core::instance::RequestDeviceError), + #[class(type)] + #[error("requiredFeatures must be a subset of the adapter features")] + RequiredFeaturesNotASubset, + #[class(inherit)] + #[error(transparent)] + Serde(#[from] serde_json::Error), + #[class("DOMExceptionOperationError")] + #[error(transparent)] + Device(#[from] wgpu_core::instance::RequestDeviceError), } pub struct GPUSupportedLimits(pub wgpu_types::Limits); -impl GarbageCollected for GPUSupportedLimits {} +impl GarbageCollected for GPUSupportedLimits { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUSupportedLimits" + } +} #[op2] impl GPUSupportedLimits { - #[getter] - fn maxTextureDimension1D(&self) -> u32 { - self.0.max_texture_dimension_1d - } - - #[getter] - fn maxTextureDimension2D(&self) -> u32 { - self.0.max_texture_dimension_2d - } - - #[getter] - fn maxTextureDimension3D(&self) -> u32 { - self.0.max_texture_dimension_3d - } - - #[getter] - fn maxTextureArrayLayers(&self) -> u32 { - self.0.max_texture_array_layers - } - - #[getter] - fn maxBindGroups(&self) -> u32 { - self.0.max_bind_groups - } - - // TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers - - #[getter] - fn maxBindingsPerBindGroup(&self) -> u32 { - self.0.max_bindings_per_bind_group - } - - #[getter] - fn maxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 { - self.0.max_dynamic_uniform_buffers_per_pipeline_layout - } - - #[getter] - fn maxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 { - self.0.max_dynamic_storage_buffers_per_pipeline_layout - } - - #[getter] - fn maxSampledTexturesPerShaderStage(&self) -> u32 { - self.0.max_sampled_textures_per_shader_stage - } - - #[getter] - fn maxSamplersPerShaderStage(&self) -> u32 { - self.0.max_samplers_per_shader_stage - } - - #[getter] - fn maxStorageBuffersPerShaderStage(&self) -> u32 { - self.0.max_storage_buffers_per_shader_stage - } - - #[getter] - fn maxStorageTexturesPerShaderStage(&self) -> u32 { - self.0.max_storage_textures_per_shader_stage - } - - #[getter] - fn maxUniformBuffersPerShaderStage(&self) -> u32 { - self.0.max_uniform_buffers_per_shader_stage - } - - #[getter] - fn maxUniformBufferBindingSize(&self) -> u32 { - self.0.max_uniform_buffer_binding_size - } - - #[getter] - fn maxStorageBufferBindingSize(&self) -> u32 { - self.0.max_storage_buffer_binding_size - } - - #[getter] - fn minUniformBufferOffsetAlignment(&self) -> u32 { - self.0.min_uniform_buffer_offset_alignment - } - - #[getter] - fn minStorageBufferOffsetAlignment(&self) -> u32 { - self.0.min_storage_buffer_offset_alignment - } - - #[getter] - fn maxVertexBuffers(&self) -> u32 { - self.0.max_vertex_buffers - } - - #[getter] - #[number] - fn maxBufferSize(&self) -> u64 { - self.0.max_buffer_size - } - - #[getter] - fn maxVertexAttributes(&self) -> u32 { - self.0.max_vertex_attributes - } - - #[getter] - fn maxVertexBufferArrayStride(&self) -> u32 { - self.0.max_vertex_buffer_array_stride - } - - // TODO(@crowlKats): support max_inter_stage_shader_variables - - #[getter] - fn maxColorAttachments(&self) -> u32 { - self.0.max_color_attachments - } - - #[getter] - fn maxColorAttachmentBytesPerSample(&self) -> u32 { - self.0.max_color_attachment_bytes_per_sample - } - - #[getter] - fn maxComputeWorkgroupStorageSize(&self) -> u32 { - self.0.max_compute_workgroup_storage_size - } - - #[getter] - fn maxComputeInvocationsPerWorkgroup(&self) -> u32 { - self.0.max_compute_invocations_per_workgroup - } - - #[getter] - fn maxComputeWorkgroupSizeX(&self) -> u32 { - self.0.max_compute_workgroup_size_x - } - - #[getter] - fn maxComputeWorkgroupSizeY(&self) -> u32 { - self.0.max_compute_workgroup_size_y - } - - #[getter] - fn maxComputeWorkgroupSizeZ(&self) -> u32 { - self.0.max_compute_workgroup_size_z - } - - #[getter] - fn maxComputeWorkgroupsPerDimension(&self) -> u32 { - self.0.max_compute_workgroups_per_dimension - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + fn maxTextureDimension1D(&self) -> u32 { + self.0.max_texture_dimension_1d + } + + #[getter] + fn maxTextureDimension2D(&self) -> u32 { + self.0.max_texture_dimension_2d + } + + #[getter] + fn maxTextureDimension3D(&self) -> u32 { + self.0.max_texture_dimension_3d + } + + #[getter] + fn maxTextureArrayLayers(&self) -> u32 { + self.0.max_texture_array_layers + } + + #[getter] + fn maxBindGroups(&self) -> u32 { + self.0.max_bind_groups + } + + // TODO(@crowlKats): support max_bind_groups_plus_vertex_buffers + + #[getter] + fn maxBindingsPerBindGroup(&self) -> u32 { + self.0.max_bindings_per_bind_group + } + + #[getter] + fn maxDynamicUniformBuffersPerPipelineLayout(&self) -> u32 { + self.0.max_dynamic_uniform_buffers_per_pipeline_layout + } + + #[getter] + fn maxDynamicStorageBuffersPerPipelineLayout(&self) -> u32 { + self.0.max_dynamic_storage_buffers_per_pipeline_layout + } + + #[getter] + fn maxSampledTexturesPerShaderStage(&self) -> u32 { + self.0.max_sampled_textures_per_shader_stage + } + + #[getter] + fn maxSamplersPerShaderStage(&self) -> u32 { + self.0.max_samplers_per_shader_stage + } + + #[getter] + fn maxStorageBuffersPerShaderStage(&self) -> u32 { + self.0.max_storage_buffers_per_shader_stage + } + + #[getter] + fn maxStorageTexturesPerShaderStage(&self) -> u32 { + self.0.max_storage_textures_per_shader_stage + } + + #[getter] + fn maxUniformBuffersPerShaderStage(&self) -> u32 { + self.0.max_uniform_buffers_per_shader_stage + } + + #[getter] + fn maxUniformBufferBindingSize(&self) -> u32 { + self.0.max_uniform_buffer_binding_size + } + + #[getter] + fn maxStorageBufferBindingSize(&self) -> u32 { + self.0.max_storage_buffer_binding_size + } + + #[getter] + fn minUniformBufferOffsetAlignment(&self) -> u32 { + self.0.min_uniform_buffer_offset_alignment + } + + #[getter] + fn minStorageBufferOffsetAlignment(&self) -> u32 { + self.0.min_storage_buffer_offset_alignment + } + + #[getter] + fn maxVertexBuffers(&self) -> u32 { + self.0.max_vertex_buffers + } + + #[getter] + #[number] + fn maxBufferSize(&self) -> u64 { + self.0.max_buffer_size + } + + #[getter] + fn maxVertexAttributes(&self) -> u32 { + self.0.max_vertex_attributes + } + + #[getter] + fn maxVertexBufferArrayStride(&self) -> u32 { + self.0.max_vertex_buffer_array_stride + } + + // TODO(@crowlKats): support max_inter_stage_shader_variables + + #[getter] + fn maxColorAttachments(&self) -> u32 { + self.0.max_color_attachments + } + + #[getter] + fn maxColorAttachmentBytesPerSample(&self) -> u32 { + self.0.max_color_attachment_bytes_per_sample + } + + #[getter] + fn maxComputeWorkgroupStorageSize(&self) -> u32 { + self.0.max_compute_workgroup_storage_size + } + + #[getter] + fn maxComputeInvocationsPerWorkgroup(&self) -> u32 { + self.0.max_compute_invocations_per_workgroup + } + + #[getter] + fn maxComputeWorkgroupSizeX(&self) -> u32 { + self.0.max_compute_workgroup_size_x + } + + #[getter] + fn maxComputeWorkgroupSizeY(&self) -> u32 { + self.0.max_compute_workgroup_size_y + } + + #[getter] + fn maxComputeWorkgroupSizeZ(&self) -> u32 { + self.0.max_compute_workgroup_size_z + } + + #[getter] + fn maxComputeWorkgroupsPerDimension(&self) -> u32 { + self.0.max_compute_workgroups_per_dimension + } } pub struct GPUSupportedFeatures(v8::Global); -impl GarbageCollected for GPUSupportedFeatures {} +impl GarbageCollected for GPUSupportedFeatures { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUSupportedFeatures" + } +} impl GPUSupportedFeatures { - #[allow(clippy::disallowed_types)] - pub fn new(scope: &mut v8::HandleScope, features: HashSet) -> Self { - let set = v8::Set::new(scope); + #[allow(clippy::disallowed_types)] + pub fn new( + scope: &mut v8::HandleScope, + features: HashSet, + ) -> Self { + let set = v8::Set::new(scope); - for feature in features { - let key = v8::String::new(scope, feature.as_str()).unwrap(); - set.add(scope, key.into()); - } - - Self(v8::Global::new(scope, >::from(set))) + for feature in features { + let key = v8::String::new(scope, feature.as_str()).unwrap(); + set.add(scope, key.into()); } + + Self(v8::Global::new(scope, >::from(set))) + } } #[op2] impl GPUSupportedFeatures { - #[global] - #[symbol("setlike_set")] - fn set(&self) -> v8::Global { - self.0.clone() - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[global] + #[symbol("setlike_set")] + fn set(&self) -> v8::Global { + self.0.clone() + } } pub struct GPUAdapterInfo { - pub info: wgpu_types::AdapterInfo, - pub subgroup_min_size: u32, - pub subgroup_max_size: u32, + pub info: wgpu_types::AdapterInfo, + pub subgroup_min_size: u32, + pub subgroup_max_size: u32, } -impl GarbageCollected for GPUAdapterInfo {} +impl GarbageCollected for GPUAdapterInfo { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUAdapterInfo" + } +} #[op2] impl GPUAdapterInfo { - #[getter] - #[string] - fn vendor(&self) -> String { - self.info.vendor.to_string() - } - - #[getter] - #[string] - fn architecture(&self) -> &'static str { - "" // TODO: wgpu#2170 - } - - #[getter] - #[string] - fn device(&self) -> String { - self.info.device.to_string() - } - - #[getter] - #[string] - fn description(&self) -> String { - self.info.name.clone() - } - - #[getter] - fn subgroup_min_size(&self) -> u32 { - self.subgroup_min_size - } - - #[getter] - fn subgroup_max_size(&self) -> u32 { - self.subgroup_max_size - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn vendor(&self) -> String { + self.info.vendor.to_string() + } + + #[getter] + #[string] + fn architecture(&self) -> &'static str { + "" // TODO: wgpu#2170 + } + + #[getter] + #[string] + fn device(&self) -> String { + self.info.device.to_string() + } + + #[getter] + #[string] + fn description(&self) -> String { + self.info.name.clone() + } + + #[getter] + fn subgroup_min_size(&self) -> u32 { + self.subgroup_min_size + } + + #[getter] + fn subgroup_max_size(&self) -> u32 { + self.subgroup_max_size + } + + #[getter] + fn is_fallback_adapter(&self) -> bool { + // TODO(lucacasonato): report correctly from wgpu + false + } } diff --git a/deno_webgpu/bind_group.rs b/deno_webgpu/bind_group.rs index f147c217801..de329ecf468 100644 --- a/deno_webgpu/bind_group.rs +++ b/deno_webgpu/bind_group.rs @@ -15,102 +15,142 @@ use deno_core::GarbageCollected; use deno_core::WebIDL; use crate::buffer::GPUBuffer; +use crate::error::GPUGenericError; use crate::sampler::GPUSampler; +use crate::texture::GPUTexture; use crate::texture::GPUTextureView; use crate::Instance; pub struct GPUBindGroup { - pub instance: Instance, - pub id: wgpu_core::id::BindGroupId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::BindGroupId, + pub label: String, } impl Drop for GPUBindGroup { - fn drop(&mut self) { - self.instance.bind_group_drop(self.id); - } + fn drop(&mut self) { + self.instance.bind_group_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUBindGroup { - const NAME: &'static str = "GPUBindGroup"; + const NAME: &'static str = "GPUBindGroup"; } -impl GarbageCollected for GPUBindGroup {} +impl GarbageCollected for GPUBindGroup { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUBindGroup" + } +} #[op2] impl GPUBindGroup { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBindGroupDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub layout: Ptr, - pub entries: Vec, + pub layout: Ptr, + pub entries: Vec, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBindGroupEntry { - #[options(enforce_range = true)] - pub binding: u32, - pub resource: GPUBindingResource, + #[options(enforce_range = true)] + pub binding: u32, + pub resource: GPUBindingResource, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBufferBinding { - pub buffer: Ptr, - #[webidl(default = 0)] - #[options(enforce_range = true)] - pub offset: u64, - #[options(enforce_range = true)] - pub size: Option, + pub buffer: Ptr, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub offset: u64, + #[options(enforce_range = true)] + pub size: Option, } pub(crate) enum GPUBindingResource { - Sampler(Ptr), - TextureView(Ptr), - BufferBinding(GPUBufferBinding), + Sampler(Ptr), + Texture(Ptr), + TextureView(Ptr), + Buffer(Ptr), + BufferBinding(GPUBufferBinding), } impl<'a> WebIdlConverter<'a> for GPUBindingResource { - type Options = (); - - fn convert<'b>( - scope: &mut HandleScope<'a>, - value: Local<'a, Value>, - prefix: Cow<'static, str>, - context: ContextFn<'b>, - options: &Self::Options, - ) -> Result { - >::convert(scope, value, prefix.clone(), context.borrowed(), options) - .map(Self::Sampler) - .or_else(|_| { - >::convert( - scope, - value, - prefix.clone(), - context.borrowed(), - options, - ) - .map(Self::TextureView) - }) - .or_else(|_| { - GPUBufferBinding::convert(scope, value, prefix, context, options) - .map(Self::BufferBinding) - }) - } + type Options = (); + + fn convert<'b>( + scope: &mut HandleScope<'a>, + value: Local<'a, Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::Sampler) + .or_else(|_| { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::Texture) + }) + .or_else(|_| { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::TextureView) + }) + .or_else(|_| { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::Buffer) + }) + .or_else(|_| { + GPUBufferBinding::convert(scope, value, prefix, context, options) + .map(Self::BufferBinding) + }) + } } diff --git a/deno_webgpu/bind_group_layout.rs b/deno_webgpu/bind_group_layout.rs index 319f626e72e..79b7bbe0bd7 100644 --- a/deno_webgpu/bind_group_layout.rs +++ b/deno_webgpu/bind_group_layout.rs @@ -4,173 +4,188 @@ use deno_core::op2; use deno_core::GarbageCollected; use deno_core::WebIDL; +use crate::error::GPUGenericError; use crate::texture::GPUTextureViewDimension; use crate::Instance; pub struct GPUBindGroupLayout { - pub instance: Instance, - pub id: wgpu_core::id::BindGroupLayoutId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::BindGroupLayoutId, + pub label: String, } impl Drop for GPUBindGroupLayout { - fn drop(&mut self) { - self.instance.bind_group_layout_drop(self.id); - } + fn drop(&mut self) { + self.instance.bind_group_layout_drop(self.id); + } } impl deno_core::webidl::WebIdlInterfaceConverter for GPUBindGroupLayout { - const NAME: &'static str = "GPUBindGroupLayout"; + const NAME: &'static str = "GPUBindGroupLayout"; } -impl GarbageCollected for GPUBindGroupLayout {} +impl GarbageCollected for GPUBindGroupLayout { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUBindGroupLayout" + } +} #[op2] impl GPUBindGroupLayout { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBindGroupLayoutDescriptor { - #[webidl(default = String::new())] - pub label: String, - pub entries: Vec, + #[webidl(default = String::new())] + pub label: String, + pub entries: Vec, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBindGroupLayoutEntry { - #[options(enforce_range = true)] - pub binding: u32, - #[options(enforce_range = true)] - pub visibility: u32, - pub buffer: Option, - pub sampler: Option, - pub texture: Option, - pub storage_texture: Option, + #[options(enforce_range = true)] + pub binding: u32, + #[options(enforce_range = true)] + pub visibility: u32, + pub buffer: Option, + pub sampler: Option, + pub texture: Option, + pub storage_texture: Option, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBufferBindingLayout { - #[webidl(default = GPUBufferBindingType::Uniform)] - pub r#type: GPUBufferBindingType, - #[webidl(default = false)] - pub has_dynamic_offset: bool, - #[webidl(default = 0)] - pub min_binding_size: u64, + #[webidl(default = GPUBufferBindingType::Uniform)] + pub r#type: GPUBufferBindingType, + #[webidl(default = false)] + pub has_dynamic_offset: bool, + #[webidl(default = 0)] + pub min_binding_size: u64, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUBufferBindingType { - Uniform, - Storage, - ReadOnlyStorage, + Uniform, + Storage, + ReadOnlyStorage, } impl From for wgpu_types::BufferBindingType { - fn from(value: GPUBufferBindingType) -> Self { - match value { - GPUBufferBindingType::Uniform => Self::Uniform, - GPUBufferBindingType::Storage => Self::Storage { read_only: false }, - GPUBufferBindingType::ReadOnlyStorage => Self::Storage { read_only: true }, - } + fn from(value: GPUBufferBindingType) -> Self { + match value { + GPUBufferBindingType::Uniform => Self::Uniform, + GPUBufferBindingType::Storage => Self::Storage { read_only: false }, + GPUBufferBindingType::ReadOnlyStorage => { + Self::Storage { read_only: true } + } } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUSamplerBindingLayout { - #[webidl(default = GPUSamplerBindingType::Filtering)] - pub r#type: GPUSamplerBindingType, + #[webidl(default = GPUSamplerBindingType::Filtering)] + pub r#type: GPUSamplerBindingType, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUSamplerBindingType { - Filtering, - NonFiltering, - Comparison, + Filtering, + NonFiltering, + Comparison, } impl From for wgpu_types::SamplerBindingType { - fn from(value: GPUSamplerBindingType) -> Self { - match value { - GPUSamplerBindingType::Filtering => Self::Filtering, - GPUSamplerBindingType::NonFiltering => Self::NonFiltering, - GPUSamplerBindingType::Comparison => Self::Comparison, - } + fn from(value: GPUSamplerBindingType) -> Self { + match value { + GPUSamplerBindingType::Filtering => Self::Filtering, + GPUSamplerBindingType::NonFiltering => Self::NonFiltering, + GPUSamplerBindingType::Comparison => Self::Comparison, } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUTextureBindingLayout { - #[webidl(default = GPUTextureSampleType::Float)] - pub sample_type: GPUTextureSampleType, - #[webidl(default = GPUTextureViewDimension::D2)] - pub view_dimension: GPUTextureViewDimension, - #[webidl(default = false)] - pub multisampled: bool, + #[webidl(default = GPUTextureSampleType::Float)] + pub sample_type: GPUTextureSampleType, + #[webidl(default = GPUTextureViewDimension::D2)] + pub view_dimension: GPUTextureViewDimension, + #[webidl(default = false)] + pub multisampled: bool, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUTextureSampleType { - Float, - UnfilterableFloat, - Depth, - Sint, - Uint, + Float, + UnfilterableFloat, + Depth, + Sint, + Uint, } impl From for wgpu_types::TextureSampleType { - fn from(value: GPUTextureSampleType) -> Self { - match value { - GPUTextureSampleType::Float => Self::Float { filterable: true }, - GPUTextureSampleType::UnfilterableFloat => Self::Float { filterable: false }, - GPUTextureSampleType::Depth => Self::Depth, - GPUTextureSampleType::Sint => Self::Sint, - GPUTextureSampleType::Uint => Self::Uint, - } + fn from(value: GPUTextureSampleType) -> Self { + match value { + GPUTextureSampleType::Float => Self::Float { filterable: true }, + GPUTextureSampleType::UnfilterableFloat => { + Self::Float { filterable: false } + } + GPUTextureSampleType::Depth => Self::Depth, + GPUTextureSampleType::Sint => Self::Sint, + GPUTextureSampleType::Uint => Self::Uint, } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUStorageTextureBindingLayout { - #[webidl(default = GPUStorageTextureAccess::WriteOnly)] - pub access: GPUStorageTextureAccess, - pub format: super::texture::GPUTextureFormat, - #[webidl(default = GPUTextureViewDimension::D2)] - pub view_dimension: GPUTextureViewDimension, + #[webidl(default = GPUStorageTextureAccess::WriteOnly)] + pub access: GPUStorageTextureAccess, + pub format: super::texture::GPUTextureFormat, + #[webidl(default = GPUTextureViewDimension::D2)] + pub view_dimension: GPUTextureViewDimension, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUStorageTextureAccess { - WriteOnly, - ReadOnly, - ReadWrite, + WriteOnly, + ReadOnly, + ReadWrite, } impl From for wgpu_types::StorageTextureAccess { - fn from(value: GPUStorageTextureAccess) -> Self { - match value { - GPUStorageTextureAccess::WriteOnly => Self::WriteOnly, - GPUStorageTextureAccess::ReadOnly => Self::ReadOnly, - GPUStorageTextureAccess::ReadWrite => Self::ReadWrite, - } + fn from(value: GPUStorageTextureAccess) -> Self { + match value { + GPUStorageTextureAccess::WriteOnly => Self::WriteOnly, + GPUStorageTextureAccess::ReadOnly => Self::ReadOnly, + GPUStorageTextureAccess::ReadWrite => Self::ReadWrite, } + } } diff --git a/deno_webgpu/buffer.rs b/deno_webgpu/buffer.rs index 6b9d12483f5..25b448a0aa5 100644 --- a/deno_webgpu/buffer.rs +++ b/deno_webgpu/buffer.rs @@ -13,245 +13,266 @@ use deno_core::WebIDL; use deno_error::JsErrorBox; use wgpu_core::device::HostMap as MapMode; +use crate::error::GPUGenericError; use crate::Instance; #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBufferDescriptor { - #[webidl(default = String::new())] - pub label: String, - - pub size: u64, - #[options(enforce_range = true)] - pub usage: u32, - #[webidl(default = false)] - pub mapped_at_creation: bool, + #[webidl(default = String::new())] + pub label: String, + + pub size: u64, + #[options(enforce_range = true)] + pub usage: u32, + #[webidl(default = false)] + pub mapped_at_creation: bool, } #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum BufferError { - #[class(generic)] - #[error(transparent)] - Canceled(#[from] oneshot::Canceled), - #[class("DOMExceptionOperationError")] - #[error(transparent)] - Access(#[from] wgpu_core::resource::BufferAccessError), - #[class("DOMExceptionOperationError")] - #[error("{0}")] - Operation(&'static str), - #[class(inherit)] - #[error(transparent)] - Other(#[from] JsErrorBox), + #[class(generic)] + #[error(transparent)] + Canceled(#[from] oneshot::Canceled), + #[class("DOMExceptionOperationError")] + #[error(transparent)] + Access(#[from] wgpu_core::resource::BufferAccessError), + #[class("DOMExceptionOperationError")] + #[error("{0}")] + Operation(&'static str), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), } pub struct GPUBuffer { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub id: wgpu_core::id::BufferId, - pub device: wgpu_core::id::DeviceId, + pub id: wgpu_core::id::BufferId, + pub device: wgpu_core::id::DeviceId, - pub label: String, + pub label: String, - pub size: u64, - pub usage: u32, + pub size: u64, + pub usage: u32, - pub map_state: RefCell<&'static str>, - pub map_mode: RefCell>, + pub map_state: RefCell<&'static str>, + pub map_mode: RefCell>, - pub mapped_js_buffers: RefCell>>, + pub mapped_js_buffers: RefCell>>, } impl Drop for GPUBuffer { - fn drop(&mut self) { - self.instance.buffer_drop(self.id); - } + fn drop(&mut self) { + self.instance.buffer_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUBuffer { - const NAME: &'static str = "GPUBuffer"; + const NAME: &'static str = "GPUBuffer"; } -impl GarbageCollected for GPUBuffer {} +impl GarbageCollected for GPUBuffer { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUBuffer" + } +} #[op2] impl GPUBuffer { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[getter] + #[number] + fn size(&self) -> u64 { + self.size + } + #[getter] + fn usage(&self) -> u32 { + self.usage + } + + #[getter] + #[string] + fn map_state(&self) -> &'static str { + *self.map_state.borrow() + } + + // In the successful case, the promise should resolve to undefined, but + // `#[undefined]` does not seem to work here. + // https://github.com/denoland/deno/issues/29603 + #[async_method] + async fn map_async( + &self, + #[webidl(options(enforce_range = true))] mode: u32, + #[webidl(default = 0)] offset: u64, + #[webidl] size: Option, + ) -> Result<(), BufferError> { + let read_mode = (mode & 0x0001) == 0x0001; + let write_mode = (mode & 0x0002) == 0x0002; + if (read_mode && write_mode) || (!read_mode && !write_mode) { + return Err(BufferError::Operation( + "exactly one of READ or WRITE map mode must be set", + )); } - #[getter] - #[number] - fn size(&self) -> u64 { - self.size - } - #[getter] - fn usage(&self) -> u32 { - self.usage - } + let mode = if read_mode { + MapMode::Read + } else { + assert!(write_mode); + MapMode::Write + }; - #[getter] - #[string] - fn map_state(&self) -> &'static str { - *self.map_state.borrow() + { + *self.map_state.borrow_mut() = "pending"; } - #[async_method] - async fn map_async( - &self, - #[webidl(options(enforce_range = true))] mode: u32, - #[webidl(default = 0)] offset: u64, - #[webidl] size: Option, - ) -> Result<(), BufferError> { - let read_mode = (mode & 0x0001) == 0x0001; - let write_mode = (mode & 0x0002) == 0x0002; - if (read_mode && write_mode) || (!read_mode && !write_mode) { - return Err(BufferError::Operation( - "exactly one of READ or WRITE map mode must be set", - )); - } - - let mode = if read_mode { - MapMode::Read - } else { - assert!(write_mode); - MapMode::Write - }; - - { - *self.map_state.borrow_mut() = "pending"; - } - - let (sender, receiver) = oneshot::channel::(); + let (sender, receiver) = + oneshot::channel::(); + + { + let callback = Box::new(move |status| { + sender.send(status).unwrap(); + }); + + let err = self + .instance + .buffer_map_async( + self.id, + offset, + size, + wgpu_core::resource::BufferMapOperation { + host: mode, + callback: Some(callback), + }, + ) + .err(); + + if err.is_some() { + self.error_handler.push_error(err); + return Err(BufferError::Operation("validation error occurred")); + } + } + let done = Rc::new(RefCell::new(false)); + let done_ = done.clone(); + let device_poll_fut = async move { + while !*done.borrow() { { - let callback = Box::new(move |status| { - sender.send(status).unwrap(); - }); - - let err = self - .instance - .buffer_map_async( - self.id, - offset, - size, - wgpu_core::resource::BufferMapOperation { - host: mode, - callback: Some(callback), - }, - ) - .err(); - - if err.is_some() { - self.error_handler.push_error(err); - return Err(BufferError::Operation("validation error occurred")); - } + self + .instance + .device_poll(self.device, wgpu_types::PollType::wait_indefinitely()) + .unwrap(); } - - let done = Rc::new(RefCell::new(false)); - let done_ = done.clone(); - let device_poll_fut = async move { - while !*done.borrow() { - { - self.instance - .device_poll(self.device, wgpu_types::PollType::wait()) - .unwrap(); - } - tokio::time::sleep(Duration::from_millis(10)).await; - } - Ok::<(), BufferError>(()) - }; - - let receiver_fut = async move { - receiver.await??; - let mut done = done_.borrow_mut(); - *done = true; - Ok::<(), BufferError>(()) - }; - - tokio::try_join!(device_poll_fut, receiver_fut)?; - - *self.map_state.borrow_mut() = "mapped"; - *self.map_mode.borrow_mut() = Some(mode); - - Ok(()) + tokio::time::sleep(Duration::from_millis(10)).await; + } + Ok::<(), BufferError>(()) + }; + + let receiver_fut = async move { + receiver.await??; + let mut done = done_.borrow_mut(); + *done = true; + Ok::<(), BufferError>(()) + }; + + tokio::try_join!(device_poll_fut, receiver_fut)?; + + *self.map_state.borrow_mut() = "mapped"; + *self.map_mode.borrow_mut() = Some(mode); + + Ok(()) + } + + fn get_mapped_range<'s>( + &self, + scope: &mut v8::HandleScope<'s>, + #[webidl(default = 0)] offset: u64, + #[webidl] size: Option, + ) -> Result, BufferError> { + let (slice_pointer, range_size) = self + .instance + .buffer_get_mapped_range(self.id, offset, size) + .map_err(BufferError::Access)?; + + let mode = self.map_mode.borrow(); + let mode = mode.as_ref().unwrap(); + + let bs = if mode == &MapMode::Write { + unsafe extern "C" fn noop_deleter_callback( + _data: *mut std::ffi::c_void, + _byte_length: usize, + _deleter_data: *mut std::ffi::c_void, + ) { + } + + // SAFETY: creating a backing store from the pointer and length provided by wgpu + unsafe { + v8::ArrayBuffer::new_backing_store_from_ptr( + slice_pointer.as_ptr() as _, + range_size as usize, + noop_deleter_callback, + std::ptr::null_mut(), + ) + } + } else { + // SAFETY: creating a vector from the pointer and length provided by wgpu + let slice = unsafe { + std::slice::from_raw_parts(slice_pointer.as_ptr(), range_size as usize) + }; + v8::ArrayBuffer::new_backing_store_from_vec(slice.to_vec()) + }; + + let shared_bs = bs.make_shared(); + let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs); + + if mode == &MapMode::Write { + self + .mapped_js_buffers + .borrow_mut() + .push(v8::Global::new(scope, ab)); } - fn get_mapped_range<'s>( - &self, - scope: &mut v8::HandleScope<'s>, - #[webidl(default = 0)] offset: u64, - #[webidl] size: Option, - ) -> Result, BufferError> { - let (slice_pointer, range_size) = self - .instance - .buffer_get_mapped_range(self.id, offset, size) - .map_err(BufferError::Access)?; - - let mode = self.map_mode.borrow(); - let mode = mode.as_ref().unwrap(); - - let bs = if mode == &MapMode::Write { - unsafe extern "C" fn noop_deleter_callback( - _data: *mut std::ffi::c_void, - _byte_length: usize, - _deleter_data: *mut std::ffi::c_void, - ) { - } - - // SAFETY: creating a backing store from the pointer and length provided by wgpu - unsafe { - v8::ArrayBuffer::new_backing_store_from_ptr( - slice_pointer.as_ptr() as _, - range_size as usize, - noop_deleter_callback, - std::ptr::null_mut(), - ) - } - } else { - // SAFETY: creating a vector from the pointer and length provided by wgpu - let slice = - unsafe { std::slice::from_raw_parts(slice_pointer.as_ptr(), range_size as usize) }; - v8::ArrayBuffer::new_backing_store_from_vec(slice.to_vec()) - }; - - let shared_bs = bs.make_shared(); - let ab = v8::ArrayBuffer::with_backing_store(scope, &shared_bs); - - if mode == &MapMode::Write { - self.mapped_js_buffers - .borrow_mut() - .push(v8::Global::new(scope, ab)); - } + Ok(ab) + } - Ok(ab) + #[nofast] + #[undefined] + fn unmap(&self, scope: &mut v8::HandleScope) -> Result<(), BufferError> { + for ab in self.mapped_js_buffers.replace(vec![]) { + let ab = ab.open(scope); + ab.detach(None); } - #[nofast] - fn unmap(&self, scope: &mut v8::HandleScope) -> Result<(), BufferError> { - for ab in self.mapped_js_buffers.replace(vec![]) { - let ab = ab.open(scope); - ab.detach(None); - } + self + .instance + .buffer_unmap(self.id) + .map_err(BufferError::Access)?; - self.instance - .buffer_unmap(self.id) - .map_err(BufferError::Access)?; + *self.map_state.borrow_mut() = "unmapped"; - *self.map_state.borrow_mut() = "unmapped"; + Ok(()) + } - Ok(()) - } - - #[fast] - fn destroy(&self) { - self.instance.buffer_destroy(self.id); - } + #[fast] + #[undefined] + fn destroy(&self) { + self.instance.buffer_destroy(self.id); + } } diff --git a/deno_webgpu/byow.rs b/deno_webgpu/byow.rs index bfce74abfdc..c6e6e71688d 100644 --- a/deno_webgpu/byow.rs +++ b/deno_webgpu/byow.rs @@ -3,10 +3,10 @@ use std::cell::RefCell; use std::ffi::c_void; #[cfg(any( - target_os = "linux", - target_os = "macos", - target_os = "freebsd", - target_os = "openbsd" + target_os = "linux", + target_os = "macos", + target_os = "freebsd", + target_os = "openbsd" ))] use std::ptr::NonNull; @@ -24,305 +24,352 @@ use crate::surface::GPUCanvasContext; #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum ByowError { - #[class(type)] - #[error("Cannot create surface outside of WebGPU context. Did you forget to call `navigator.gpu.requestAdapter()`?")] - WebGPUNotInitiated, - #[class(type)] - #[error("Invalid parameters")] - InvalidParameters, - #[class(generic)] - #[error(transparent)] - CreateSurface(wgpu_core::instance::CreateSurfaceError), - #[cfg(target_os = "windows")] - #[class(type)] - #[error("Invalid system on Windows")] - InvalidSystem, - #[cfg(target_os = "macos")] - #[class(type)] - #[error("Invalid system on macOS")] - InvalidSystem, - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] - #[class(type)] - #[error("Invalid system on Linux/BSD")] - InvalidSystem, - #[cfg(any( - target_os = "windows", - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd" - ))] - #[class(type)] - #[error("window is null")] - NullWindow, - #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] - #[class(type)] - #[error("display is null")] - NullDisplay, - #[cfg(target_os = "macos")] - #[class(type)] - #[error("ns_view is null")] - NSViewDisplay, + #[cfg(not(any( + target_os = "macos", + target_os = "windows", + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", + )))] + #[class(type)] + #[error("Unsupported platform")] + Unsupported, + #[class(type)] + #[error( + "Cannot create surface outside of WebGPU context. Did you forget to call `navigator.gpu.requestAdapter()`?" + )] + WebGPUNotInitiated, + #[class(type)] + #[error("Invalid parameters")] + InvalidParameters, + #[class(generic)] + #[error(transparent)] + CreateSurface(wgpu_core::instance::CreateSurfaceError), + #[cfg(target_os = "windows")] + #[class(type)] + #[error("Invalid system on Windows")] + InvalidSystem, + #[cfg(target_os = "macos")] + #[class(type)] + #[error("Invalid system on macOS")] + InvalidSystem, + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd" + ))] + #[class(type)] + #[error("Invalid system on Linux/BSD")] + InvalidSystem, + #[cfg(any( + target_os = "windows", + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd" + ))] + #[class(type)] + #[error("window is null")] + NullWindow, + #[cfg(any( + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd" + ))] + #[class(type)] + #[error("display is null")] + NullDisplay, + #[cfg(target_os = "macos")] + #[class(type)] + #[error("ns_view is null")] + NSViewDisplay, } // TODO(@littledivy): This will extend `OffscreenCanvas` when we add it. pub struct UnsafeWindowSurface { - pub id: wgpu_core::id::SurfaceId, - pub width: RefCell, - pub height: RefCell, + pub id: wgpu_core::id::SurfaceId, + pub width: RefCell, + pub height: RefCell, - pub context: SameObject, + pub context: SameObject, } -impl GarbageCollected for UnsafeWindowSurface {} +impl GarbageCollected for UnsafeWindowSurface { + fn get_name(&self) -> &'static std::ffi::CStr { + c"UnsafeWindowSurface" + } +} #[op2] impl UnsafeWindowSurface { - #[constructor] - #[cppgc] - fn new( - state: &mut OpState, - #[from_v8] options: UnsafeWindowSurfaceOptions, - ) -> Result { - let instance = state - .try_borrow::() - .ok_or(ByowError::WebGPUNotInitiated)?; - - // Security note: - // - // The `window_handle` and `display_handle` options are pointers to - // platform-specific window handles. - // - // The code below works under the assumption that: - // - // - handles can only be created by the FFI interface which - // enforces --allow-ffi. - // - // - `*const c_void` deserizalizes null and v8::External. - // - // - Only FFI can export v8::External to user code. - if options.window_handle.is_null() { - return Err(ByowError::InvalidParameters); - } - - let (win_handle, display_handle) = raw_window( - options.system, - options.window_handle, - options.display_handle, - )?; - - // SAFETY: see above comment - let id = unsafe { - instance - .instance_create_surface(display_handle, win_handle, None) - .map_err(ByowError::CreateSurface)? - }; - - Ok(UnsafeWindowSurface { - id, - width: RefCell::new(options.width), - height: RefCell::new(options.height), - context: SameObject::new(), - }) + #[constructor] + #[cppgc] + fn new( + state: &mut OpState, + #[from_v8] options: UnsafeWindowSurfaceOptions, + ) -> Result { + let instance = state + .try_borrow::() + .ok_or(ByowError::WebGPUNotInitiated)?; + + // Security note: + // + // The `window_handle` and `display_handle` options are pointers to + // platform-specific window handles. + // + // The code below works under the assumption that: + // + // - handles can only be created by the FFI interface which + // enforces --allow-ffi. + // + // - `*const c_void` deserizalizes null and v8::External. + // + // - Only FFI can export v8::External to user code. + if options.window_handle.is_null() { + return Err(ByowError::InvalidParameters); } - #[global] - fn get_context( - &self, - #[this] this: v8::Global, - scope: &mut v8::HandleScope, - ) -> v8::Global { - self.context.get(scope, |_| GPUCanvasContext { - surface_id: self.id, - width: self.width.clone(), - height: self.height.clone(), - config: RefCell::new(None), - texture: RefCell::new(None), - canvas: this, - }) - } + let (win_handle, display_handle) = raw_window( + options.system, + options.window_handle, + options.display_handle, + )?; + + // SAFETY: see above comment + let id = unsafe { + instance + .instance_create_surface(display_handle, win_handle, None) + .map_err(ByowError::CreateSurface)? + }; + + Ok(UnsafeWindowSurface { + id, + width: RefCell::new(options.width), + height: RefCell::new(options.height), + context: SameObject::new(), + }) + } + + #[global] + fn get_context( + &self, + #[this] this: v8::Global, + scope: &mut v8::HandleScope, + ) -> v8::Global { + self.context.get(scope, |_| GPUCanvasContext { + surface_id: self.id, + width: self.width.clone(), + height: self.height.clone(), + config: RefCell::new(None), + texture: RefCell::new(None), + canvas: this, + }) + } + + #[nofast] + fn present(&self, scope: &mut v8::HandleScope) -> Result<(), JsErrorBox> { + let Some(context) = self.context.try_unwrap(scope) else { + return Err(JsErrorBox::type_error("getContext was never called")); + }; - #[nofast] - fn present(&self, scope: &mut v8::HandleScope) -> Result<(), JsErrorBox> { - let Some(context) = self.context.try_unwrap(scope) else { - return Err(JsErrorBox::type_error("getContext was never called")); - }; + context.present().map_err(JsErrorBox::from_err) + } - context.present().map_err(JsErrorBox::from_err) - } + #[fast] + fn resize(&self, width: u32, height: u32, scope: &mut v8::HandleScope) { + self.width.replace(width); + self.height.replace(height); + + let Some(context) = self.context.try_unwrap(scope) else { + return; + }; + + context.resize_configure(width, height); + } } struct UnsafeWindowSurfaceOptions { - system: UnsafeWindowSurfaceSystem, - window_handle: *const c_void, - display_handle: *const c_void, - width: u32, - height: u32, + system: UnsafeWindowSurfaceSystem, + window_handle: *const c_void, + display_handle: *const c_void, + width: u32, + height: u32, } #[derive(Eq, PartialEq)] enum UnsafeWindowSurfaceSystem { - Cocoa, - Win32, - X11, - Wayland, + Cocoa, + Win32, + X11, + Wayland, } impl<'a> FromV8<'a> for UnsafeWindowSurfaceOptions { - type Error = JsErrorBox; - - fn from_v8( - scope: &mut v8::HandleScope<'a>, - value: Local<'a, Value>, - ) -> Result { - let obj = value - .try_cast::() - .map_err(|_| JsErrorBox::type_error("is not an object"))?; - - let key = v8::String::new(scope, "system").unwrap(); - let val = obj - .get(scope, key.into()) - .ok_or_else(|| JsErrorBox::type_error("missing field 'system'"))?; - let s = String::from_v8(scope, val).unwrap(); - let system = match s.as_str() { - "cocoa" => UnsafeWindowSurfaceSystem::Cocoa, - "win32" => UnsafeWindowSurfaceSystem::Win32, - "x11" => UnsafeWindowSurfaceSystem::X11, - "wayland" => UnsafeWindowSurfaceSystem::Wayland, - _ => return Err(JsErrorBox::type_error(format!("Invalid system kind '{s}'"))), - }; - - let key = v8::String::new(scope, "windowHandle").unwrap(); - let val = obj - .get(scope, key.into()) - .ok_or_else(|| JsErrorBox::type_error("missing field 'windowHandle'"))?; - let Some(window_handle) = deno_core::_ops::to_external_option(&val) else { - return Err(JsErrorBox::type_error("expected external")); - }; - - let key = v8::String::new(scope, "displayHandle").unwrap(); - let val = obj - .get(scope, key.into()) - .ok_or_else(|| JsErrorBox::type_error("missing field 'displayHandle'"))?; - let Some(display_handle) = deno_core::_ops::to_external_option(&val) else { - return Err(JsErrorBox::type_error("expected external")); - }; - - let key = v8::String::new(scope, "width").unwrap(); - let val = obj - .get(scope, key.into()) - .ok_or_else(|| JsErrorBox::type_error("missing field 'width'"))?; - let width = deno_core::convert::Number::::from_v8(scope, val)?.0; - - let key = v8::String::new(scope, "height").unwrap(); - let val = obj - .get(scope, key.into()) - .ok_or_else(|| JsErrorBox::type_error("missing field 'height'"))?; - let height = deno_core::convert::Number::::from_v8(scope, val)?.0; - - Ok(Self { - system, - window_handle, - display_handle, - width, - height, - }) - } + type Error = JsErrorBox; + + fn from_v8( + scope: &mut v8::HandleScope<'a>, + value: Local<'a, Value>, + ) -> Result { + let obj = value + .try_cast::() + .map_err(|_| JsErrorBox::type_error("is not an object"))?; + + let key = v8::String::new(scope, "system").unwrap(); + let val = obj + .get(scope, key.into()) + .ok_or_else(|| JsErrorBox::type_error("missing field 'system'"))?; + let s = String::from_v8(scope, val).unwrap(); + let system = match s.as_str() { + "cocoa" => UnsafeWindowSurfaceSystem::Cocoa, + "win32" => UnsafeWindowSurfaceSystem::Win32, + "x11" => UnsafeWindowSurfaceSystem::X11, + "wayland" => UnsafeWindowSurfaceSystem::Wayland, + _ => { + return Err(JsErrorBox::type_error(format!( + "Invalid system kind '{s}'" + ))); + } + }; + + let key = v8::String::new(scope, "windowHandle").unwrap(); + let val = obj + .get(scope, key.into()) + .ok_or_else(|| JsErrorBox::type_error("missing field 'windowHandle'"))?; + let Some(window_handle) = deno_core::_ops::to_external_option(&val) else { + return Err(JsErrorBox::type_error("expected external")); + }; + + let key = v8::String::new(scope, "displayHandle").unwrap(); + let val = obj + .get(scope, key.into()) + .ok_or_else(|| JsErrorBox::type_error("missing field 'displayHandle'"))?; + let Some(display_handle) = deno_core::_ops::to_external_option(&val) else { + return Err(JsErrorBox::type_error("expected external")); + }; + + let key = v8::String::new(scope, "width").unwrap(); + let val = obj + .get(scope, key.into()) + .ok_or_else(|| JsErrorBox::type_error("missing field 'width'"))?; + let width = deno_core::convert::Number::::from_v8(scope, val)?.0; + + let key = v8::String::new(scope, "height").unwrap(); + let val = obj + .get(scope, key.into()) + .ok_or_else(|| JsErrorBox::type_error("missing field 'height'"))?; + let height = deno_core::convert::Number::::from_v8(scope, val)?.0; + + Ok(Self { + system, + window_handle, + display_handle, + width, + height, + }) + } } type RawHandles = ( - raw_window_handle::RawWindowHandle, - raw_window_handle::RawDisplayHandle, + raw_window_handle::RawWindowHandle, + raw_window_handle::RawDisplayHandle, ); #[cfg(target_os = "macos")] fn raw_window( - system: UnsafeWindowSurfaceSystem, - _ns_window: *const c_void, - ns_view: *const c_void, + system: UnsafeWindowSurfaceSystem, + _ns_window: *const c_void, + ns_view: *const c_void, ) -> Result { - if system != UnsafeWindowSurfaceSystem::Cocoa { - return Err(ByowError::InvalidSystem); - } - - let win_handle = - raw_window_handle::RawWindowHandle::AppKit(raw_window_handle::AppKitWindowHandle::new( - NonNull::new(ns_view as *mut c_void).ok_or(ByowError::NSViewDisplay)?, - )); - - let display_handle = - raw_window_handle::RawDisplayHandle::AppKit(raw_window_handle::AppKitDisplayHandle::new()); - Ok((win_handle, display_handle)) + if system != UnsafeWindowSurfaceSystem::Cocoa { + return Err(ByowError::InvalidSystem); + } + + let win_handle = raw_window_handle::RawWindowHandle::AppKit( + raw_window_handle::AppKitWindowHandle::new( + NonNull::new(ns_view as *mut c_void).ok_or(ByowError::NSViewDisplay)?, + ), + ); + + let display_handle = raw_window_handle::RawDisplayHandle::AppKit( + raw_window_handle::AppKitDisplayHandle::new(), + ); + Ok((win_handle, display_handle)) } #[cfg(target_os = "windows")] fn raw_window( - system: UnsafeWindowSurfaceSystem, - window: *const c_void, - hinstance: *const c_void, + system: UnsafeWindowSurfaceSystem, + window: *const c_void, + hinstance: *const c_void, ) -> Result { - use raw_window_handle::WindowsDisplayHandle; - if system != UnsafeWindowSurfaceSystem::Win32 { - return Err(ByowError::InvalidSystem); - } - - let win_handle = { - let mut handle = raw_window_handle::Win32WindowHandle::new( - std::num::NonZeroIsize::new(window as isize).ok_or(ByowError::NullWindow)?, - ); - handle.hinstance = std::num::NonZeroIsize::new(hinstance as isize); - - raw_window_handle::RawWindowHandle::Win32(handle) - }; - - let display_handle = raw_window_handle::RawDisplayHandle::Windows(WindowsDisplayHandle::new()); - Ok((win_handle, display_handle)) + use raw_window_handle::WindowsDisplayHandle; + if system != UnsafeWindowSurfaceSystem::Win32 { + return Err(ByowError::InvalidSystem); + } + + let win_handle = { + let mut handle = raw_window_handle::Win32WindowHandle::new( + std::num::NonZeroIsize::new(window as isize) + .ok_or(ByowError::NullWindow)?, + ); + handle.hinstance = std::num::NonZeroIsize::new(hinstance as isize); + + raw_window_handle::RawWindowHandle::Win32(handle) + }; + + let display_handle = + raw_window_handle::RawDisplayHandle::Windows(WindowsDisplayHandle::new()); + Ok((win_handle, display_handle)) } #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))] fn raw_window( - system: UnsafeWindowSurfaceSystem, - window: *const c_void, - display: *const c_void, + system: UnsafeWindowSurfaceSystem, + window: *const c_void, + display: *const c_void, ) -> Result { - let (win_handle, display_handle); - if system == UnsafeWindowSurfaceSystem::X11 { - win_handle = raw_window_handle::RawWindowHandle::Xlib( - raw_window_handle::XlibWindowHandle::new(window as *mut c_void as _), - ); - - display_handle = raw_window_handle::RawDisplayHandle::Xlib( - raw_window_handle::XlibDisplayHandle::new(NonNull::new(display as *mut c_void), 0), - ); - } else if system == UnsafeWindowSurfaceSystem::Wayland { - win_handle = raw_window_handle::RawWindowHandle::Wayland( - raw_window_handle::WaylandWindowHandle::new( - NonNull::new(window as *mut c_void).ok_or(ByowError::NullWindow)?, - ), - ); - - display_handle = raw_window_handle::RawDisplayHandle::Wayland( - raw_window_handle::WaylandDisplayHandle::new( - NonNull::new(display as *mut c_void).ok_or(ByowError::NullDisplay)?, - ), - ); - } else { - return Err(ByowError::InvalidSystem); - } - - Ok((win_handle, display_handle)) + let (win_handle, display_handle); + if system == UnsafeWindowSurfaceSystem::X11 { + win_handle = raw_window_handle::RawWindowHandle::Xlib( + raw_window_handle::XlibWindowHandle::new(window as *mut c_void as _), + ); + + display_handle = raw_window_handle::RawDisplayHandle::Xlib( + raw_window_handle::XlibDisplayHandle::new( + NonNull::new(display as *mut c_void), + 0, + ), + ); + } else if system == UnsafeWindowSurfaceSystem::Wayland { + win_handle = raw_window_handle::RawWindowHandle::Wayland( + raw_window_handle::WaylandWindowHandle::new( + NonNull::new(window as *mut c_void).ok_or(ByowError::NullWindow)?, + ), + ); + + display_handle = raw_window_handle::RawDisplayHandle::Wayland( + raw_window_handle::WaylandDisplayHandle::new( + NonNull::new(display as *mut c_void).ok_or(ByowError::NullDisplay)?, + ), + ); + } else { + return Err(ByowError::InvalidSystem); + } + + Ok((win_handle, display_handle)) } #[cfg(not(any( - target_os = "macos", - target_os = "windows", - target_os = "linux", - target_os = "freebsd", - target_os = "openbsd", + target_os = "macos", + target_os = "windows", + target_os = "linux", + target_os = "freebsd", + target_os = "openbsd", )))] fn raw_window( - _system: UnsafeWindowSurfaceSystem, - _window: *const c_void, - _display: *const c_void, -) -> Result { - Err(deno_error::JsErrorBox::type_error("Unsupported platform")) + _system: UnsafeWindowSurfaceSystem, + _window: *const c_void, + _display: *const c_void, +) -> Result { + Err(ByowError::Unsupported) } diff --git a/deno_webgpu/command_buffer.rs b/deno_webgpu/command_buffer.rs index b89795d6545..6ad6dbc09bf 100644 --- a/deno_webgpu/command_buffer.rs +++ b/deno_webgpu/command_buffer.rs @@ -4,43 +4,54 @@ use deno_core::op2; use deno_core::GarbageCollected; use deno_core::WebIDL; +use crate::error::GPUGenericError; use crate::Instance; pub struct GPUCommandBuffer { - pub instance: Instance, - pub id: wgpu_core::id::CommandBufferId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::CommandBufferId, + pub label: String, } impl Drop for GPUCommandBuffer { - fn drop(&mut self) { - self.instance.command_buffer_drop(self.id); - } + fn drop(&mut self) { + self.instance.command_buffer_drop(self.id); + } } impl deno_core::webidl::WebIdlInterfaceConverter for GPUCommandBuffer { - const NAME: &'static str = "GPUCommandBuffer"; + const NAME: &'static str = "GPUCommandBuffer"; } -impl GarbageCollected for GPUCommandBuffer {} +impl GarbageCollected for GPUCommandBuffer { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUCommandBuffer" + } +} #[op2] impl GPUCommandBuffer { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUCommandBufferDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, } diff --git a/deno_webgpu/command_encoder.rs b/deno_webgpu/command_encoder.rs index 318c52a7102..3c9a37c9907 100644 --- a/deno_webgpu/command_encoder.rs +++ b/deno_webgpu/command_encoder.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use std::cell::RefCell; -use std::sync::atomic::{AtomicBool, Ordering}; use deno_core::cppgc::Ptr; use deno_core::op2; @@ -17,6 +16,7 @@ use wgpu_types::{BufferAddress, TexelCopyBufferInfo}; use crate::buffer::GPUBuffer; use crate::command_buffer::GPUCommandBuffer; use crate::compute_pass::GPUComputePassEncoder; +use crate::error::GPUGenericError; use crate::queue::GPUTexelCopyTextureInfo; use crate::render_pass::GPULoadOp; use crate::render_pass::GPURenderPassEncoder; @@ -24,69 +24,73 @@ use crate::webidl::GPUExtent3D; use crate::Instance; pub struct GPUCommandEncoder { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub id: wgpu_core::id::CommandEncoderId, - pub label: String, - - pub finished: AtomicBool, + pub id: wgpu_core::id::CommandEncoderId, + pub label: String, } impl Drop for GPUCommandEncoder { - fn drop(&mut self) { - // Command encoders and command buffers are both the same wgpu object. - // At the time `finished` is set, ownership of the id (and - // responsibility for dropping it) transfers from the encoder to the - // buffer. - if !self.finished.load(Ordering::SeqCst) { - self.instance.command_encoder_drop(self.id); - } - } + fn drop(&mut self) { + self.instance.command_encoder_drop(self.id); + } } -impl GarbageCollected for GPUCommandEncoder {} +impl GarbageCollected for GPUCommandEncoder { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUCommandEncoder" + } +} #[op2] impl GPUCommandEncoder { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[required(1)] - #[cppgc] - fn begin_render_pass( - &self, - #[webidl] descriptor: crate::render_pass::GPURenderPassDescriptor, - ) -> Result { - let color_attachments = Cow::Owned( - descriptor - .color_attachments - .into_iter() - .map(|attachment| { - attachment.into_option().map(|attachment| { - wgpu_core::command::RenderPassColorAttachment { - view: attachment.view.id, - depth_slice: attachment.depth_slice, - resolve_target: attachment.resolve_target.map(|target| target.id), - load_op: attachment - .load_op - .with_default_value(attachment.clear_value.map(Into::into)), - store_op: attachment.store_op.into(), - } - }) - }) - .collect::>(), - ); + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[required(1)] + #[cppgc] + fn begin_render_pass( + &self, + #[webidl] descriptor: crate::render_pass::GPURenderPassDescriptor, + ) -> Result { + let color_attachments = Cow::Owned( + descriptor + .color_attachments + .into_iter() + .map(|attachment| { + attachment.into_option().map(|attachment| { + wgpu_core::command::RenderPassColorAttachment { + view: attachment.view.to_view_id(), + depth_slice: attachment.depth_slice, + resolve_target: attachment + .resolve_target + .map(|target| target.to_view_id()), + load_op: attachment + .load_op + .with_default_value(attachment.clear_value.map(Into::into)), + store_op: attachment.store_op.into(), + } + }) + }) + .collect::>(), + ); - let depth_stencil_attachment = descriptor + let depth_stencil_attachment = descriptor .depth_stencil_attachment .map(|attachment| { if attachment @@ -101,7 +105,7 @@ impl GPUCommandEncoder { } Ok(wgpu_core::command::RenderPassDepthStencilAttachment { - view: attachment.view.id, + view: attachment.view.to_view_id(), depth: PassChannel { load_op: attachment .depth_load_op @@ -120,371 +124,372 @@ impl GPUCommandEncoder { }) .transpose()?; - let timestamp_writes = descriptor.timestamp_writes.map(|timestamp_writes| { - wgpu_core::command::PassTimestampWrites { - query_set: timestamp_writes.query_set.id, - beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index, - end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, - } - }); - - let wgpu_descriptor = wgpu_core::command::RenderPassDescriptor { - label: crate::transform_label(descriptor.label.clone()), - color_attachments, - depth_stencil_attachment: depth_stencil_attachment.as_ref(), - timestamp_writes: timestamp_writes.as_ref(), - occlusion_query_set: descriptor.occlusion_query_set.map(|query_set| query_set.id), - }; - - let (render_pass, err) = self - .instance - .command_encoder_begin_render_pass(self.id, &wgpu_descriptor); - - self.error_handler.push_error(err); - - Ok(GPURenderPassEncoder { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - render_pass: RefCell::new(render_pass), - label: descriptor.label, - }) - } - - #[cppgc] - fn begin_compute_pass( - &self, - #[webidl] descriptor: crate::compute_pass::GPUComputePassDescriptor, - ) -> GPUComputePassEncoder { - let timestamp_writes = descriptor.timestamp_writes.map(|timestamp_writes| { - wgpu_core::command::PassTimestampWrites { - query_set: timestamp_writes.query_set.id, - beginning_of_pass_write_index: timestamp_writes.beginning_of_pass_write_index, - end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, - } - }); - - let wgpu_descriptor = wgpu_core::command::ComputePassDescriptor { - label: crate::transform_label(descriptor.label.clone()), - timestamp_writes, - }; - - let (compute_pass, err) = self - .instance - .command_encoder_begin_compute_pass(self.id, &wgpu_descriptor); - - self.error_handler.push_error(err); - - GPUComputePassEncoder { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - compute_pass: RefCell::new(compute_pass), - label: descriptor.label, + let timestamp_writes = + descriptor.timestamp_writes.map(|timestamp_writes| { + wgpu_core::command::PassTimestampWrites { + query_set: timestamp_writes.query_set.id, + beginning_of_pass_write_index: timestamp_writes + .beginning_of_pass_write_index, + end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, } - } - - #[required(2)] - fn copy_buffer_to_buffer<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - #[webidl] source: Ptr, - arg2: v8::Local<'a, v8::Value>, - arg3: v8::Local<'a, v8::Value>, - arg4: v8::Local<'a, v8::Value>, - arg5: v8::Local<'a, v8::Value>, - ) -> Result<(), WebIdlError> { - let prefix = "Failed to execute 'GPUCommandEncoder.copyBufferToBuffer'"; - let int_options = IntOptions { - clamp: false, - enforce_range: true, - }; - - let source_offset: BufferAddress; - let destination: Ptr; - let destination_offset: BufferAddress; - let size: Option; - // Note that the last argument to either overload of `copy_buffer_to_buffer` - // is optional, so `arg5.is_undefined()` would not work here. - if arg4.is_undefined() { - // 3-argument overload - source_offset = 0; - destination = Ptr::::convert( - scope, - arg2, - Cow::Borrowed(prefix), - (|| Cow::Borrowed("destination")).into(), - &(), - )?; - destination_offset = 0; - size = >::convert( - scope, - arg3, - Cow::Borrowed(prefix), - (|| Cow::Borrowed("size")).into(), - &int_options, - )?; - } else { - // 5-argument overload - source_offset = u64::convert( - scope, - arg2, - Cow::Borrowed(prefix), - (|| Cow::Borrowed("sourceOffset")).into(), - &int_options, - )?; - destination = Ptr::::convert( - scope, - arg3, - Cow::Borrowed(prefix), - (|| Cow::Borrowed("destination")).into(), - &(), - )?; - destination_offset = u64::convert( - scope, - arg4, - Cow::Borrowed(prefix), - (|| Cow::Borrowed("destinationOffset")).into(), - &int_options, - )?; - size = >::convert( - scope, - arg5, - Cow::Borrowed(prefix), - (|| Cow::Borrowed("size")).into(), - &int_options, - )?; + }); + + let wgpu_descriptor = wgpu_core::command::RenderPassDescriptor { + label: crate::transform_label(descriptor.label.clone()), + color_attachments, + depth_stencil_attachment: depth_stencil_attachment.as_ref(), + timestamp_writes: timestamp_writes.as_ref(), + occlusion_query_set: descriptor + .occlusion_query_set + .map(|query_set| query_set.id), + }; + + let (render_pass, err) = self + .instance + .command_encoder_begin_render_pass(self.id, &wgpu_descriptor); + + self.error_handler.push_error(err); + + Ok(GPURenderPassEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + render_pass: RefCell::new(render_pass), + label: descriptor.label, + }) + } + + #[cppgc] + fn begin_compute_pass( + &self, + #[webidl] descriptor: crate::compute_pass::GPUComputePassDescriptor, + ) -> GPUComputePassEncoder { + let timestamp_writes = + descriptor.timestamp_writes.map(|timestamp_writes| { + wgpu_core::command::PassTimestampWrites { + query_set: timestamp_writes.query_set.id, + beginning_of_pass_write_index: timestamp_writes + .beginning_of_pass_write_index, + end_of_pass_write_index: timestamp_writes.end_of_pass_write_index, } + }); - let err = self - .instance - .command_encoder_copy_buffer_to_buffer( - self.id, - source.id, - source_offset, - destination.id, - destination_offset, - size, - ) - .err(); - - self.error_handler.push_error(err); - - Ok(()) - } - - #[required(3)] - fn copy_buffer_to_texture( - &self, - #[webidl] source: GPUTexelCopyBufferInfo, - #[webidl] destination: GPUTexelCopyTextureInfo, - #[webidl] copy_size: GPUExtent3D, - ) { - let source = TexelCopyBufferInfo { - buffer: source.buffer.id, - layout: wgpu_types::TexelCopyBufferLayout { - offset: source.offset, - bytes_per_row: source.bytes_per_row, - rows_per_image: source.rows_per_image, - }, - }; - let destination = wgpu_types::TexelCopyTextureInfo { - texture: destination.texture.id, - mip_level: destination.mip_level, - origin: destination.origin.into(), - aspect: destination.aspect.into(), - }; - - let err = self - .instance - .command_encoder_copy_buffer_to_texture( - self.id, - &source, - &destination, - ©_size.into(), - ) - .err(); - - self.error_handler.push_error(err); - } - - #[required(3)] - fn copy_texture_to_buffer( - &self, - #[webidl] source: GPUTexelCopyTextureInfo, - #[webidl] destination: GPUTexelCopyBufferInfo, - #[webidl] copy_size: GPUExtent3D, - ) { - let source = wgpu_types::TexelCopyTextureInfo { - texture: source.texture.id, - mip_level: source.mip_level, - origin: source.origin.into(), - aspect: source.aspect.into(), - }; - let destination = TexelCopyBufferInfo { - buffer: destination.buffer.id, - layout: wgpu_types::TexelCopyBufferLayout { - offset: destination.offset, - bytes_per_row: destination.bytes_per_row, - rows_per_image: destination.rows_per_image, - }, - }; - - let err = self - .instance - .command_encoder_copy_texture_to_buffer( - self.id, - &source, - &destination, - ©_size.into(), - ) - .err(); - - self.error_handler.push_error(err); - } + let wgpu_descriptor = wgpu_core::command::ComputePassDescriptor { + label: crate::transform_label(descriptor.label.clone()), + timestamp_writes, + }; - #[required(3)] - fn copy_texture_to_texture( - &self, - #[webidl] source: GPUTexelCopyTextureInfo, - #[webidl] destination: GPUTexelCopyTextureInfo, - #[webidl] copy_size: GPUExtent3D, - ) { - let source = wgpu_types::TexelCopyTextureInfo { - texture: source.texture.id, - mip_level: source.mip_level, - origin: source.origin.into(), - aspect: source.aspect.into(), - }; - let destination = wgpu_types::TexelCopyTextureInfo { - texture: destination.texture.id, - mip_level: destination.mip_level, - origin: destination.origin.into(), - aspect: destination.aspect.into(), - }; - - let err = self - .instance - .command_encoder_copy_texture_to_texture( - self.id, - &source, - &destination, - ©_size.into(), - ) - .err(); - - self.error_handler.push_error(err); - } + let (compute_pass, err) = self + .instance + .command_encoder_begin_compute_pass(self.id, &wgpu_descriptor); - #[required(1)] - fn clear_buffer( - &self, - #[webidl] buffer: Ptr, - #[webidl(default = 0, options(enforce_range = true))] offset: u64, - #[webidl(options(enforce_range = true))] size: Option, - ) { - let err = self - .instance - .command_encoder_clear_buffer(self.id, buffer.id, offset, size) - .err(); - self.error_handler.push_error(err); - } + self.error_handler.push_error(err); - #[required(5)] - fn resolve_query_set( - &self, - #[webidl] query_set: Ptr, - #[webidl(options(enforce_range = true))] first_query: u32, - #[webidl(options(enforce_range = true))] query_count: u32, - #[webidl] destination: Ptr, - #[webidl(options(enforce_range = true))] destination_offset: u64, - ) { - let err = self - .instance - .command_encoder_resolve_query_set( - self.id, - query_set.id, - first_query, - query_count, - destination.id, - destination_offset, - ) - .err(); - - self.error_handler.push_error(err); + GPUComputePassEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + compute_pass: RefCell::new(compute_pass), + label: descriptor.label, } - - #[cppgc] - fn finish( - &self, - #[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor, - ) -> Result { - let wgpu_descriptor = wgpu_types::CommandBufferDescriptor { - label: crate::transform_label(descriptor.label.clone()), - }; - - // TODO(https://github.com/gfx-rs/wgpu/issues/7812): This is not right, - // it should be a validation error, and it would be nice if we can just - // let wgpu generate it for us. The problem is that if the encoder was - // already finished, we transferred ownership of the id to a command - // buffer, so we have to bail out before we mint a duplicate command - // buffer with the same id below. - if self.finished.fetch_or(true, Ordering::SeqCst) { - return Err(JsErrorBox::type_error( - "The command encoder has already finished.", - )); - } - - let (id, err) = self - .instance - .command_encoder_finish(self.id, &wgpu_descriptor); - - self.error_handler.push_error(err); - - Ok(GPUCommandBuffer { - instance: self.instance.clone(), - id, - label: descriptor.label, - }) - } - - fn push_debug_group(&self, #[webidl] group_label: String) { - let err = self - .instance - .command_encoder_push_debug_group(self.id, &group_label) - .err(); - self.error_handler.push_error(err); - } - - #[fast] - fn pop_debug_group(&self) { - let err = self.instance.command_encoder_pop_debug_group(self.id).err(); - self.error_handler.push_error(err); + } + + #[required(2)] + #[undefined] + fn copy_buffer_to_buffer<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl] source: Ptr, + arg2: v8::Local<'a, v8::Value>, + arg3: v8::Local<'a, v8::Value>, + arg4: v8::Local<'a, v8::Value>, + arg5: v8::Local<'a, v8::Value>, + ) -> Result<(), WebIdlError> { + let prefix = "Failed to execute 'GPUCommandEncoder.copyBufferToBuffer'"; + let int_options = IntOptions { + clamp: false, + enforce_range: true, + }; + + let source_offset: BufferAddress; + let destination: Ptr; + let destination_offset: BufferAddress; + let size: Option; + // Note that the last argument to either overload of `copy_buffer_to_buffer` + // is optional, so `arg5.is_undefined()` would not work here. + if arg4.is_undefined() { + // 3-argument overload + source_offset = 0; + destination = Ptr::::convert( + scope, + arg2, + Cow::Borrowed(prefix), + (|| Cow::Borrowed("destination")).into(), + &(), + )?; + destination_offset = 0; + size = >::convert( + scope, + arg3, + Cow::Borrowed(prefix), + (|| Cow::Borrowed("size")).into(), + &int_options, + )?; + } else { + // 5-argument overload + source_offset = u64::convert( + scope, + arg2, + Cow::Borrowed(prefix), + (|| Cow::Borrowed("sourceOffset")).into(), + &int_options, + )?; + destination = Ptr::::convert( + scope, + arg3, + Cow::Borrowed(prefix), + (|| Cow::Borrowed("destination")).into(), + &(), + )?; + destination_offset = u64::convert( + scope, + arg4, + Cow::Borrowed(prefix), + (|| Cow::Borrowed("destinationOffset")).into(), + &int_options, + )?; + size = >::convert( + scope, + arg5, + Cow::Borrowed(prefix), + (|| Cow::Borrowed("size")).into(), + &int_options, + )?; } - fn insert_debug_marker(&self, #[webidl] marker_label: String) { - let err = self - .instance - .command_encoder_insert_debug_marker(self.id, &marker_label) - .err(); - self.error_handler.push_error(err); + let err = self + .instance + .command_encoder_copy_buffer_to_buffer( + self.id, + source.id, + source_offset, + destination.id, + destination_offset, + size, + ) + .err(); + + self.error_handler.push_error(err); + + Ok(()) + } + + #[required(3)] + #[undefined] + fn copy_buffer_to_texture( + &self, + #[webidl] source: GPUTexelCopyBufferInfo, + #[webidl] destination: GPUTexelCopyTextureInfo, + #[webidl] copy_size: GPUExtent3D, + ) { + let source = TexelCopyBufferInfo { + buffer: source.buffer.id, + layout: wgpu_types::TexelCopyBufferLayout { + offset: source.offset, + bytes_per_row: source.bytes_per_row, + rows_per_image: source.rows_per_image, + }, + }; + let destination = wgpu_types::TexelCopyTextureInfo { + texture: destination.texture.id, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let err = self + .instance + .command_encoder_copy_buffer_to_texture( + self.id, + &source, + &destination, + ©_size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } + + #[required(3)] + #[undefined] + fn copy_texture_to_buffer( + &self, + #[webidl] source: GPUTexelCopyTextureInfo, + #[webidl] destination: GPUTexelCopyBufferInfo, + #[webidl] copy_size: GPUExtent3D, + ) { + let source = wgpu_types::TexelCopyTextureInfo { + texture: source.texture.id, + mip_level: source.mip_level, + origin: source.origin.into(), + aspect: source.aspect.into(), + }; + let destination = TexelCopyBufferInfo { + buffer: destination.buffer.id, + layout: wgpu_types::TexelCopyBufferLayout { + offset: destination.offset, + bytes_per_row: destination.bytes_per_row, + rows_per_image: destination.rows_per_image, + }, + }; + + let err = self + .instance + .command_encoder_copy_texture_to_buffer( + self.id, + &source, + &destination, + ©_size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } + + #[required(3)] + #[undefined] + fn copy_texture_to_texture( + &self, + #[webidl] source: GPUTexelCopyTextureInfo, + #[webidl] destination: GPUTexelCopyTextureInfo, + #[webidl] copy_size: GPUExtent3D, + ) { + let source = wgpu_types::TexelCopyTextureInfo { + texture: source.texture.id, + mip_level: source.mip_level, + origin: source.origin.into(), + aspect: source.aspect.into(), + }; + let destination = wgpu_types::TexelCopyTextureInfo { + texture: destination.texture.id, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let err = self + .instance + .command_encoder_copy_texture_to_texture( + self.id, + &source, + &destination, + ©_size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn clear_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let err = self + .instance + .command_encoder_clear_buffer(self.id, buffer.id, offset, size) + .err(); + self.error_handler.push_error(err); + } + + #[required(5)] + #[undefined] + fn resolve_query_set( + &self, + #[webidl] query_set: Ptr, + #[webidl(options(enforce_range = true))] first_query: u32, + #[webidl(options(enforce_range = true))] query_count: u32, + #[webidl] destination: Ptr, + #[webidl(options(enforce_range = true))] destination_offset: u64, + ) { + let err = self + .instance + .command_encoder_resolve_query_set( + self.id, + query_set.id, + first_query, + query_count, + destination.id, + destination_offset, + ) + .err(); + + self.error_handler.push_error(err); + } + + #[cppgc] + fn finish( + &self, + #[webidl] descriptor: crate::command_buffer::GPUCommandBufferDescriptor, + ) -> GPUCommandBuffer { + let wgpu_descriptor = wgpu_types::CommandBufferDescriptor { + label: crate::transform_label(descriptor.label.clone()), + }; + + let (id, err) = + self + .instance + .command_encoder_finish(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + GPUCommandBuffer { + instance: self.instance.clone(), + id, + label: descriptor.label, } + } + + fn push_debug_group(&self, #[webidl] group_label: String) { + let err = self + .instance + .command_encoder_push_debug_group(self.id, &group_label) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + fn pop_debug_group(&self) { + let err = self.instance.command_encoder_pop_debug_group(self.id).err(); + self.error_handler.push_error(err); + } + + fn insert_debug_marker(&self, #[webidl] marker_label: String) { + let err = self + .instance + .command_encoder_insert_debug_marker(self.id, &marker_label) + .err(); + self.error_handler.push_error(err); + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUCommandEncoderDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUTexelCopyBufferInfo { - pub buffer: Ptr, - #[webidl(default = 0)] - #[options(enforce_range = true)] - offset: u64, - #[options(enforce_range = true)] - bytes_per_row: Option, - #[options(enforce_range = true)] - rows_per_image: Option, + pub buffer: Ptr, + #[webidl(default = 0)] + #[options(enforce_range = true)] + offset: u64, + #[options(enforce_range = true)] + bytes_per_row: Option, + #[options(enforce_range = true)] + rows_per_image: Option, } diff --git a/deno_webgpu/compute_pass.rs b/deno_webgpu/compute_pass.rs index 4cd060818f0..50dffae29b4 100644 --- a/deno_webgpu/compute_pass.rs +++ b/deno_webgpu/compute_pass.rs @@ -13,208 +13,240 @@ use deno_core::webidl::WebIdlError; use deno_core::GarbageCollected; use deno_core::WebIDL; +use crate::error::GPUGenericError; use crate::Instance; pub struct GPUComputePassEncoder { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub compute_pass: RefCell, - pub label: String, + pub compute_pass: RefCell, + pub label: String, } -impl GarbageCollected for GPUComputePassEncoder {} +impl GarbageCollected for GPUComputePassEncoder { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUComputePassEncoder" + } +} #[op2] impl GPUComputePassEncoder { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - fn set_pipeline(&self, #[webidl] pipeline: Ptr) { - let err = self - .instance - .compute_pass_set_pipeline(&mut self.compute_pass.borrow_mut(), pipeline.id) - .err(); - self.error_handler.push_error(err); - } - - fn dispatch_workgroups( - &self, - #[webidl(options(enforce_range = true))] work_group_count_x: u32, - #[webidl(default = 1, options(enforce_range = true))] work_group_count_y: u32, - #[webidl(default = 1, options(enforce_range = true))] work_group_count_z: u32, - ) { - let err = self - .instance - .compute_pass_dispatch_workgroups( - &mut self.compute_pass.borrow_mut(), - work_group_count_x, - work_group_count_y, - work_group_count_z, - ) - .err(); - self.error_handler.push_error(err); - } - - fn dispatch_workgroups_indirect( - &self, - #[webidl] indirect_buffer: Ptr, - #[webidl(options(enforce_range = true))] indirect_offset: u64, - ) { - let err = self - .instance - .compute_pass_dispatch_workgroups_indirect( - &mut self.compute_pass.borrow_mut(), - indirect_buffer.id, - indirect_offset, - ) - .err(); - self.error_handler.push_error(err); - } - - #[fast] - fn end(&self) { - let err = self - .instance - .compute_pass_end(&mut self.compute_pass.borrow_mut()) - .err(); - self.error_handler.push_error(err); - } - - fn push_debug_group(&self, #[webidl] group_label: String) { - let err = self - .instance - .compute_pass_push_debug_group( - &mut self.compute_pass.borrow_mut(), - &group_label, - 0, // wgpu#975 - ) - .err(); - self.error_handler.push_error(err); - } - - #[fast] - fn pop_debug_group(&self) { - let err = self - .instance - .compute_pass_pop_debug_group(&mut self.compute_pass.borrow_mut()) - .err(); - self.error_handler.push_error(err); - } - - fn insert_debug_marker(&self, #[webidl] marker_label: String) { - let err = self - .instance - .compute_pass_insert_debug_marker( - &mut self.compute_pass.borrow_mut(), - &marker_label, - 0, // wgpu#975 - ) - .err(); - self.error_handler.push_error(err); - } - - fn set_bind_group<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - #[webidl(options(enforce_range = true))] index: u32, - #[webidl] bind_group: Nullable>, - dynamic_offsets: v8::Local<'a, v8::Value>, - dynamic_offsets_data_start: v8::Local<'a, v8::Value>, - dynamic_offsets_data_length: v8::Local<'a, v8::Value>, - ) -> Result<(), WebIdlError> { - const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; - let err = if let Ok(uint_32) = dynamic_offsets.try_cast::() { - let start = u64::convert( - scope, - dynamic_offsets_data_start, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 4")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? as usize; - let len = u32::convert( - scope, - dynamic_offsets_data_length, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 5")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? as usize; - - let ab = uint_32.buffer(scope).unwrap(); - let ptr = ab.data().unwrap(); - let ab_len = ab.byte_length() / 4; - - // SAFETY: compute_pass_set_bind_group internally calls extend_from_slice with this slice - let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; - - let offsets = &data[start..(start + len)]; - - self.instance - .compute_pass_set_bind_group( - &mut self.compute_pass.borrow_mut(), - index, - bind_group.into_option().map(|bind_group| bind_group.id), - offsets, - ) - .err() - } else { - let offsets = >>::convert( - scope, - dynamic_offsets, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 3")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? - .unwrap_or_default(); - - self.instance - .compute_pass_set_bind_group( - &mut self.compute_pass.borrow_mut(), - index, - bind_group.into_option().map(|bind_group| bind_group.id), - &offsets, - ) - .err() - }; - - self.error_handler.push_error(err); - - Ok(()) - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[undefined] + fn set_pipeline( + &self, + #[webidl] pipeline: Ptr, + ) { + let err = self + .instance + .compute_pass_set_pipeline( + &mut self.compute_pass.borrow_mut(), + pipeline.id, + ) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn dispatch_workgroups( + &self, + #[webidl(options(enforce_range = true))] work_group_count_x: u32, + #[webidl(default = 1, options(enforce_range = true))] + work_group_count_y: u32, + #[webidl(default = 1, options(enforce_range = true))] + work_group_count_z: u32, + ) { + let err = self + .instance + .compute_pass_dispatch_workgroups( + &mut self.compute_pass.borrow_mut(), + work_group_count_x, + work_group_count_y, + work_group_count_z, + ) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn dispatch_workgroups_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) { + let err = self + .instance + .compute_pass_dispatch_workgroups_indirect( + &mut self.compute_pass.borrow_mut(), + indirect_buffer.id, + indirect_offset, + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + #[undefined] + fn end(&self) { + let err = self + .instance + .compute_pass_end(&mut self.compute_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn push_debug_group(&self, #[webidl] group_label: String) { + let err = self + .instance + .compute_pass_push_debug_group( + &mut self.compute_pass.borrow_mut(), + &group_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + #[undefined] + fn pop_debug_group(&self) { + let err = self + .instance + .compute_pass_pop_debug_group(&mut self.compute_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn insert_debug_marker(&self, #[webidl] marker_label: String) { + let err = self + .instance + .compute_pass_insert_debug_marker( + &mut self.compute_pass.borrow_mut(), + &marker_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn set_bind_group<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl(options(enforce_range = true))] index: u32, + #[webidl] bind_group: Nullable>, + dynamic_offsets: v8::Local<'a, v8::Value>, + dynamic_offsets_data_start: v8::Local<'a, v8::Value>, + dynamic_offsets_data_length: v8::Local<'a, v8::Value>, + ) -> Result<(), WebIdlError> { + const PREFIX: &str = + "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; + let err = if let Ok(uint_32) = dynamic_offsets.try_cast::() + { + let start = u64::convert( + scope, + dynamic_offsets_data_start, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 4")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + let len = u32::convert( + scope, + dynamic_offsets_data_length, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 5")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + + let ab = uint_32.buffer(scope).unwrap(); + let ptr = ab.data().unwrap(); + let ab_len = ab.byte_length() / 4; + + // SAFETY: compute_pass_set_bind_group internally calls extend_from_slice with this slice + let data = + unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; + + let offsets = &data[start..(start + len)]; + + self + .instance + .compute_pass_set_bind_group( + &mut self.compute_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets, + ) + .err() + } else { + let offsets = >>::convert( + scope, + dynamic_offsets, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 3")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? + .unwrap_or_default(); + + self + .instance + .compute_pass_set_bind_group( + &mut self.compute_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + &offsets, + ) + .err() + }; + + self.error_handler.push_error(err); + + Ok(()) + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUComputePassDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub timestamp_writes: Option, + pub timestamp_writes: Option, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUComputePassTimestampWrites { - pub query_set: Ptr, - #[options(enforce_range = true)] - pub beginning_of_pass_write_index: Option, - #[options(enforce_range = true)] - pub end_of_pass_write_index: Option, + pub query_set: Ptr, + #[options(enforce_range = true)] + pub beginning_of_pass_write_index: Option, + #[options(enforce_range = true)] + pub end_of_pass_write_index: Option, } diff --git a/deno_webgpu/compute_pipeline.rs b/deno_webgpu/compute_pipeline.rs index eb29686bce5..bd7a9695986 100644 --- a/deno_webgpu/compute_pipeline.rs +++ b/deno_webgpu/compute_pipeline.rs @@ -8,75 +8,86 @@ use deno_core::WebIDL; use indexmap::IndexMap; use crate::bind_group_layout::GPUBindGroupLayout; +use crate::error::GPUGenericError; use crate::shader::GPUShaderModule; use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode; use crate::Instance; pub struct GPUComputePipeline { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub id: wgpu_core::id::ComputePipelineId, - pub label: String, + pub id: wgpu_core::id::ComputePipelineId, + pub label: String, } impl Drop for GPUComputePipeline { - fn drop(&mut self) { - self.instance.compute_pipeline_drop(self.id); - } + fn drop(&mut self) { + self.instance.compute_pipeline_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUComputePipeline { - const NAME: &'static str = "GPUComputePipeline"; + const NAME: &'static str = "GPUComputePipeline"; } -impl GarbageCollected for GPUComputePipeline {} +impl GarbageCollected for GPUComputePipeline { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUComputePipeline" + } +} #[op2] impl GPUComputePipeline { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } - #[cppgc] - fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout { - let (id, err) = self - .instance - .compute_pipeline_get_bind_group_layout(self.id, index, None); + #[cppgc] + fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout { + let (id, err) = self + .instance + .compute_pipeline_get_bind_group_layout(self.id, index, None); - self.error_handler.push_error(err); + self.error_handler.push_error(err); - // TODO(wgpu): needs to support retrieving the label - GPUBindGroupLayout { - instance: self.instance.clone(), - id, - label: "".to_string(), - } + // TODO(wgpu): needs to support retrieving the label + GPUBindGroupLayout { + instance: self.instance.clone(), + id, + label: "".to_string(), } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUComputePipelineDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub compute: GPUProgrammableStage, - pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode, + pub compute: GPUProgrammableStage, + pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUProgrammableStage { - pub module: Ptr, - pub entry_point: Option, - #[webidl(default = Default::default())] - pub constants: IndexMap, + pub module: Ptr, + pub entry_point: Option, + #[webidl(default = Default::default())] + pub constants: IndexMap, } diff --git a/deno_webgpu/device.rs b/deno_webgpu/device.rs index 5416f53798c..b59fc633cd4 100644 --- a/deno_webgpu/device.rs +++ b/deno_webgpu/device.rs @@ -30,7 +30,7 @@ use crate::adapter::GPUAdapterInfo; use crate::adapter::GPUSupportedFeatures; use crate::adapter::GPUSupportedLimits; use crate::command_encoder::GPUCommandEncoder; -use crate::error::GPUError; +use crate::error::{GPUError, GPUGenericError}; use crate::query_set::GPUQuerySet; use crate::render_bundle::GPURenderBundleEncoder; use crate::render_pipeline::GPURenderPipeline; @@ -39,839 +39,932 @@ use crate::webidl::features_to_feature_names; use crate::Instance; pub struct GPUDevice { - pub instance: Instance, - pub id: wgpu_core::id::DeviceId, - pub adapter: wgpu_core::id::AdapterId, - pub queue: wgpu_core::id::QueueId, + pub instance: Instance, + pub id: wgpu_core::id::DeviceId, + pub adapter: wgpu_core::id::AdapterId, + pub queue: wgpu_core::id::QueueId, - pub label: String, + pub label: String, - pub features: SameObject, - pub limits: SameObject, - pub adapter_info: Rc>, + pub features: SameObject, + pub limits: SameObject, + pub adapter_info: Rc>, - pub queue_obj: SameObject, + pub queue_obj: SameObject, - pub error_handler: super::error::ErrorHandler, - pub lost_promise: v8::Global, + pub error_handler: super::error::ErrorHandler, + pub lost_promise: v8::Global, } impl Drop for GPUDevice { - fn drop(&mut self) { - self.instance.device_drop(self.id); - } + fn drop(&mut self) { + self.instance.device_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUDevice { - const NAME: &'static str = "GPUDevice"; + const NAME: &'static str = "GPUDevice"; } -impl GarbageCollected for GPUDevice {} +impl GarbageCollected for GPUDevice { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUDevice" + } +} // EventTarget is extended in JS #[op2] impl GPUDevice { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[getter] - #[global] - fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.features.get(scope, |scope| { - let features = self.instance.device_features(self.id); - let features = features_to_feature_names(features); - GPUSupportedFeatures::new(scope, features) - }) - } - - #[getter] - #[global] - fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.limits.get(scope, |_| { - let limits = self.instance.device_limits(self.id); - GPUSupportedLimits(limits) - }) - } - - #[getter] - #[global] - fn adapter_info(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.adapter_info.get(scope, |_| { - let info = self.instance.adapter_get_info(self.adapter); - let limits = self.instance.adapter_limits(self.adapter); - - GPUAdapterInfo { - info, - subgroup_min_size: limits.min_subgroup_size, - subgroup_max_size: limits.max_subgroup_size, - } - }) - } - - #[getter] - #[global] - fn queue(&self, scope: &mut v8::HandleScope) -> v8::Global { - self.queue_obj.get(scope, |_| GPUQueue { - id: self.queue, - device: self.id, - error_handler: self.error_handler.clone(), - instance: self.instance.clone(), - label: self.label.clone(), - }) - } - - #[fast] - fn destroy(&self) { - self.instance.device_destroy(self.id); - self.error_handler - .push_error(Some(GPUError::Lost(GPUDeviceLostReason::Destroyed))); - } - - #[required(1)] - #[cppgc] - fn create_buffer(&self, #[webidl] descriptor: super::buffer::GPUBufferDescriptor) -> GPUBuffer { - // Validation of the usage needs to happen on the device timeline, so - // don't raise an error immediately if it isn't valid. wgpu will - // reject `BufferUsages::empty()`. - let usage = wgpu_types::BufferUsages::from_bits(descriptor.usage) - .unwrap_or(wgpu_types::BufferUsages::empty()); - - let wgpu_descriptor = wgpu_core::resource::BufferDescriptor { - label: crate::transform_label(descriptor.label.clone()), - size: descriptor.size, - usage, - mapped_at_creation: descriptor.mapped_at_creation, - }; - - let (id, err) = self - .instance - .device_create_buffer(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - GPUBuffer { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - id, - device: self.id, - label: descriptor.label, - size: descriptor.size, - usage: descriptor.usage, - map_state: RefCell::new(if descriptor.mapped_at_creation { - "mapped" - } else { - "unmapped" - }), - map_mode: RefCell::new(if descriptor.mapped_at_creation { - Some(wgpu_core::device::HostMap::Write) - } else { - None - }), - mapped_js_buffers: RefCell::new(vec![]), + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[getter] + #[global] + fn features(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.features.get(scope, |scope| { + let features = self.instance.device_features(self.id); + let features = features_to_feature_names(features); + GPUSupportedFeatures::new(scope, features) + }) + } + + #[getter] + #[global] + fn limits(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.limits.get(scope, |_| { + let limits = self.instance.device_limits(self.id); + GPUSupportedLimits(limits) + }) + } + + #[getter] + #[global] + fn adapter_info( + &self, + scope: &mut v8::HandleScope, + ) -> v8::Global { + self.adapter_info.get(scope, |_| { + let info = self.instance.adapter_get_info(self.adapter); + let limits = self.instance.adapter_limits(self.adapter); + + GPUAdapterInfo { + info, + subgroup_min_size: limits.min_subgroup_size, + subgroup_max_size: limits.max_subgroup_size, + } + }) + } + + #[getter] + #[global] + fn queue(&self, scope: &mut v8::HandleScope) -> v8::Global { + self.queue_obj.get(scope, |_| GPUQueue { + id: self.queue, + device: self.id, + error_handler: self.error_handler.clone(), + instance: self.instance.clone(), + label: self.label.clone(), + }) + } + + #[fast] + #[undefined] + fn destroy(&self) { + self.instance.device_destroy(self.id); + self + .error_handler + .push_error(Some(GPUError::Lost(GPUDeviceLostReason::Destroyed))); + } + + #[required(1)] + #[cppgc] + fn create_buffer( + &self, + #[webidl] descriptor: super::buffer::GPUBufferDescriptor, + ) -> GPUBuffer { + // Validation of the usage needs to happen on the device timeline, so + // don't raise an error immediately if it isn't valid. wgpu will + // reject `BufferUsages::empty()`. + let usage = wgpu_types::BufferUsages::from_bits(descriptor.usage) + .unwrap_or(wgpu_types::BufferUsages::empty()); + + let wgpu_descriptor = wgpu_core::resource::BufferDescriptor { + label: crate::transform_label(descriptor.label.clone()), + size: descriptor.size, + usage, + mapped_at_creation: descriptor.mapped_at_creation, + }; + + let (id, err) = + self + .instance + .device_create_buffer(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + GPUBuffer { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + device: self.id, + label: descriptor.label, + size: descriptor.size, + usage: descriptor.usage, + map_state: RefCell::new(if descriptor.mapped_at_creation { + "mapped" + } else { + "unmapped" + }), + map_mode: RefCell::new(if descriptor.mapped_at_creation { + Some(wgpu_core::device::HostMap::Write) + } else { + None + }), + mapped_js_buffers: RefCell::new(vec![]), + } + } + + #[required(1)] + #[cppgc] + fn create_texture( + &self, + #[webidl] descriptor: super::texture::GPUTextureDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::TextureDescriptor { + label: crate::transform_label(descriptor.label.clone()), + size: descriptor.size.into(), + mip_level_count: descriptor.mip_level_count, + sample_count: descriptor.sample_count, + dimension: descriptor.dimension.clone().into(), + format: descriptor.format.clone().into(), + usage: wgpu_types::TextureUsages::from_bits(descriptor.usage) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, + view_formats: descriptor + .view_formats + .into_iter() + .map(Into::into) + .collect(), + }; + + let (id, err) = + self + .instance + .device_create_texture(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUTexture { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + default_view_id: Default::default(), + label: descriptor.label, + size: wgpu_descriptor.size, + mip_level_count: wgpu_descriptor.mip_level_count, + sample_count: wgpu_descriptor.sample_count, + dimension: descriptor.dimension, + format: descriptor.format, + usage: descriptor.usage, + }) + } + + #[cppgc] + fn create_sampler( + &self, + #[webidl] descriptor: super::sampler::GPUSamplerDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::SamplerDescriptor { + label: crate::transform_label(descriptor.label.clone()), + address_modes: [ + descriptor.address_mode_u.into(), + descriptor.address_mode_v.into(), + descriptor.address_mode_w.into(), + ], + mag_filter: descriptor.mag_filter.into(), + min_filter: descriptor.min_filter.into(), + mipmap_filter: descriptor.mipmap_filter.into(), + lod_min_clamp: descriptor.lod_min_clamp, + lod_max_clamp: descriptor.lod_max_clamp, + compare: descriptor.compare.map(Into::into), + anisotropy_clamp: descriptor.max_anisotropy, + border_color: None, + }; + + let (id, err) = + self + .instance + .device_create_sampler(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUSampler { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } + + #[required(1)] + #[cppgc] + fn create_bind_group_layout( + &self, + #[webidl] + descriptor: super::bind_group_layout::GPUBindGroupLayoutDescriptor, + ) -> Result { + let mut entries = Vec::with_capacity(descriptor.entries.len()); + + for entry in descriptor.entries { + let n_entries = [ + entry.buffer.is_some(), + entry.sampler.is_some(), + entry.texture.is_some(), + entry.storage_texture.is_some(), + ] + .into_iter() + .filter(|t| *t) + .count(); + + if n_entries != 1 { + return Err(JsErrorBox::type_error( + "Only one of 'buffer', 'sampler', 'texture' and 'storageTexture' may be specified", + )); + } + + let ty = if let Some(buffer) = entry.buffer { + BindingType::Buffer { + ty: buffer.r#type.into(), + has_dynamic_offset: buffer.has_dynamic_offset, + min_binding_size: NonZeroU64::new(buffer.min_binding_size), } - } - - #[required(1)] - #[cppgc] - fn create_texture( - &self, - #[webidl] descriptor: super::texture::GPUTextureDescriptor, - ) -> Result { - let wgpu_descriptor = wgpu_core::resource::TextureDescriptor { - label: crate::transform_label(descriptor.label.clone()), - size: descriptor.size.into(), - mip_level_count: descriptor.mip_level_count, - sample_count: descriptor.sample_count, - dimension: descriptor.dimension.clone().into(), - format: descriptor.format.clone().into(), - usage: wgpu_types::TextureUsages::from_bits(descriptor.usage) - .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, - view_formats: descriptor - .view_formats - .into_iter() - .map(Into::into) - .collect(), - }; - - let (id, err) = self - .instance - .device_create_texture(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - Ok(GPUTexture { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - id, - label: descriptor.label, - size: wgpu_descriptor.size, - mip_level_count: wgpu_descriptor.mip_level_count, - sample_count: wgpu_descriptor.sample_count, - dimension: descriptor.dimension, - format: descriptor.format, - usage: descriptor.usage, - }) - } - - #[cppgc] - fn create_sampler( - &self, - #[webidl] descriptor: super::sampler::GPUSamplerDescriptor, - ) -> Result { - let wgpu_descriptor = wgpu_core::resource::SamplerDescriptor { - label: crate::transform_label(descriptor.label.clone()), - address_modes: [ - descriptor.address_mode_u.into(), - descriptor.address_mode_v.into(), - descriptor.address_mode_w.into(), - ], - mag_filter: descriptor.mag_filter.into(), - min_filter: descriptor.min_filter.into(), - mipmap_filter: descriptor.mipmap_filter.into(), - lod_min_clamp: descriptor.lod_min_clamp, - lod_max_clamp: descriptor.lod_max_clamp, - compare: descriptor.compare.map(Into::into), - anisotropy_clamp: descriptor.max_anisotropy, - border_color: None, - }; - - let (id, err) = self - .instance - .device_create_sampler(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - Ok(GPUSampler { - instance: self.instance.clone(), - id, - label: descriptor.label, - }) - } - - #[required(1)] - #[cppgc] - fn create_bind_group_layout( - &self, - #[webidl] descriptor: super::bind_group_layout::GPUBindGroupLayoutDescriptor, - ) -> Result { - let mut entries = Vec::with_capacity(descriptor.entries.len()); - - for entry in descriptor.entries { - let n_entries = [ - entry.buffer.is_some(), - entry.sampler.is_some(), - entry.texture.is_some(), - entry.storage_texture.is_some(), - ] - .into_iter() - .filter(|t| *t) - .count(); - - if n_entries != 1 { - return Err(JsErrorBox::type_error("Only one of 'buffer', 'sampler', 'texture' and 'storageTexture' may be specified")); - } - - let ty = if let Some(buffer) = entry.buffer { - BindingType::Buffer { - ty: buffer.r#type.into(), - has_dynamic_offset: buffer.has_dynamic_offset, - min_binding_size: NonZeroU64::new(buffer.min_binding_size), - } - } else if let Some(sampler) = entry.sampler { - BindingType::Sampler(sampler.r#type.into()) - } else if let Some(texture) = entry.texture { - BindingType::Texture { - sample_type: texture.sample_type.into(), - view_dimension: texture.view_dimension.into(), - multisampled: texture.multisampled, - } - } else if let Some(storage_texture) = entry.storage_texture { - BindingType::StorageTexture { - access: storage_texture.access.into(), - format: storage_texture.format.into(), - view_dimension: storage_texture.view_dimension.into(), - } - } else { - unreachable!() - }; - - entries.push(wgpu_types::BindGroupLayoutEntry { - binding: entry.binding, - visibility: wgpu_types::ShaderStages::from_bits(entry.visibility) - .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, - ty, - count: None, // native-only - }); + } else if let Some(sampler) = entry.sampler { + BindingType::Sampler(sampler.r#type.into()) + } else if let Some(texture) = entry.texture { + BindingType::Texture { + sample_type: texture.sample_type.into(), + view_dimension: texture.view_dimension.into(), + multisampled: texture.multisampled, } - - let wgpu_descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor { - label: crate::transform_label(descriptor.label.clone()), - entries: Cow::Owned(entries), - }; - - let (id, err) = - self.instance - .device_create_bind_group_layout(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - Ok(GPUBindGroupLayout { - instance: self.instance.clone(), - id, - label: descriptor.label, - }) - } - - #[required(1)] - #[cppgc] - fn create_pipeline_layout( - &self, - #[webidl] descriptor: super::pipeline_layout::GPUPipelineLayoutDescriptor, - ) -> GPUPipelineLayout { - let bind_group_layouts = descriptor - .bind_group_layouts - .into_iter() - .map(|bind_group_layout| bind_group_layout.id) - .collect(); - - let wgpu_descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { - label: crate::transform_label(descriptor.label.clone()), - bind_group_layouts: Cow::Owned(bind_group_layouts), - push_constant_ranges: Default::default(), - }; - - let (id, err) = - self.instance - .device_create_pipeline_layout(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - GPUPipelineLayout { - instance: self.instance.clone(), - id, - label: descriptor.label, + } else if let Some(storage_texture) = entry.storage_texture { + BindingType::StorageTexture { + access: storage_texture.access.into(), + format: storage_texture.format.into(), + view_dimension: storage_texture.view_dimension.into(), } - } - - #[required(1)] - #[cppgc] - fn create_bind_group( - &self, - #[webidl] descriptor: super::bind_group::GPUBindGroupDescriptor, - ) -> GPUBindGroup { - let entries = descriptor - .entries - .into_iter() - .map(|entry| wgpu_core::binding_model::BindGroupEntry { - binding: entry.binding, - resource: match entry.resource { - GPUBindingResource::Sampler(sampler) => BindingResource::Sampler(sampler.id), - GPUBindingResource::TextureView(texture_view) => { - BindingResource::TextureView(texture_view.id) - } - GPUBindingResource::BufferBinding(buffer_binding) => { - BindingResource::Buffer(wgpu_core::binding_model::BufferBinding { - buffer: buffer_binding.buffer.id, - offset: buffer_binding.offset, - size: buffer_binding.size.and_then(NonZeroU64::new), - }) - } - }, + } else { + unreachable!() + }; + + entries.push(wgpu_types::BindGroupLayoutEntry { + binding: entry.binding, + visibility: wgpu_types::ShaderStages::from_bits(entry.visibility) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, + ty, + count: None, // native-only + }); + } + + let wgpu_descriptor = wgpu_core::binding_model::BindGroupLayoutDescriptor { + label: crate::transform_label(descriptor.label.clone()), + entries: Cow::Owned(entries), + }; + + let (id, err) = self.instance.device_create_bind_group_layout( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + Ok(GPUBindGroupLayout { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } + + #[required(1)] + #[cppgc] + fn create_pipeline_layout( + &self, + #[webidl] descriptor: super::pipeline_layout::GPUPipelineLayoutDescriptor, + ) -> GPUPipelineLayout { + let bind_group_layouts = descriptor + .bind_group_layouts + .into_iter() + .map(|bind_group_layout| bind_group_layout.id) + .collect(); + + let wgpu_descriptor = wgpu_core::binding_model::PipelineLayoutDescriptor { + label: crate::transform_label(descriptor.label.clone()), + bind_group_layouts: Cow::Owned(bind_group_layouts), + push_constant_ranges: Default::default(), + }; + + let (id, err) = self.instance.device_create_pipeline_layout( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + GPUPipelineLayout { + instance: self.instance.clone(), + id, + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_bind_group( + &self, + #[webidl] descriptor: super::bind_group::GPUBindGroupDescriptor, + ) -> GPUBindGroup { + let entries = descriptor + .entries + .into_iter() + .map(|entry| wgpu_core::binding_model::BindGroupEntry { + binding: entry.binding, + resource: match entry.resource { + GPUBindingResource::Sampler(sampler) => { + BindingResource::Sampler(sampler.id) + } + GPUBindingResource::Texture(texture) => { + BindingResource::TextureView(texture.default_view_id()) + } + GPUBindingResource::TextureView(texture_view) => { + BindingResource::TextureView(texture_view.id) + } + GPUBindingResource::Buffer(buffer) => { + BindingResource::Buffer(wgpu_core::binding_model::BufferBinding { + buffer: buffer.id, + offset: 0, + size: NonZeroU64::new(buffer.size), }) - .collect::>(); - - let wgpu_descriptor = wgpu_core::binding_model::BindGroupDescriptor { - label: crate::transform_label(descriptor.label.clone()), - layout: descriptor.layout.id, - entries: Cow::Owned(entries), - }; - - let (id, err) = self - .instance - .device_create_bind_group(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - GPUBindGroup { - instance: self.instance.clone(), - id, - label: descriptor.label, - } - } - - #[required(1)] - #[cppgc] - fn create_shader_module( - &self, - scope: &mut v8::HandleScope<'_>, - #[webidl] descriptor: super::shader::GPUShaderModuleDescriptor, - ) -> GPUShaderModule { - let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor { - label: crate::transform_label(descriptor.label.clone()), - runtime_checks: wgpu_types::ShaderRuntimeChecks::default(), - }; - - let (id, err) = self.instance.device_create_shader_module( - self.id, - &wgpu_descriptor, - wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed(&descriptor.code)), - None, - ); - - let compilation_info = GPUCompilationInfo::new(scope, err.iter(), &descriptor.code); - let compilation_info = make_cppgc_object(scope, compilation_info); - let compilation_info = v8::Global::new(scope, compilation_info); - self.error_handler.push_error(err); - - GPUShaderModule { - instance: self.instance.clone(), - id, - label: descriptor.label, - compilation_info, - } - } - - #[required(1)] - #[cppgc] - fn create_compute_pipeline( - &self, - #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, - ) -> GPUComputePipeline { - self.new_compute_pipeline(descriptor) - } - - #[required(1)] - #[cppgc] - fn create_render_pipeline( - &self, - #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor, - ) -> Result { - self.new_render_pipeline(descriptor) - } - - #[async_method] - #[required(1)] - #[cppgc] - async fn create_compute_pipeline_async( - &self, - #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, - ) -> GPUComputePipeline { - self.new_compute_pipeline(descriptor) - } - - #[async_method] - #[required(1)] - #[cppgc] - async fn create_render_pipeline_async( - &self, - #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor, - ) -> Result { - self.new_render_pipeline(descriptor) - } - - #[cppgc] - fn create_command_encoder( - &self, - #[webidl] descriptor: Option, - ) -> GPUCommandEncoder { - let label = descriptor.map(|d| d.label).unwrap_or_default(); - let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor { - label: Some(Cow::Owned(label.clone())), - }; - - let (id, err) = - self.instance - .device_create_command_encoder(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - GPUCommandEncoder { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - id, - label, - finished: Default::default(), - } - } - - #[required(1)] - #[cppgc] - fn create_render_bundle_encoder( - &self, - #[webidl] descriptor: super::render_bundle::GPURenderBundleEncoderDescriptor, - ) -> GPURenderBundleEncoder { - let wgpu_descriptor = wgpu_core::command::RenderBundleEncoderDescriptor { - label: crate::transform_label(descriptor.label.clone()), - color_formats: Cow::Owned( - descriptor - .color_formats - .into_iter() - .map(|format| format.into_option().map(Into::into)) - .collect::>(), - ), - depth_stencil: descriptor.depth_stencil_format.map(|format| { - wgpu_types::RenderBundleDepthStencil { - format: format.into(), - depth_read_only: descriptor.depth_read_only, - stencil_read_only: descriptor.stencil_read_only, - } - }), - sample_count: descriptor.sample_count, - multiview: None, - }; - - let res = wgpu_core::command::RenderBundleEncoder::new(&wgpu_descriptor, self.id, None); - let (encoder, err) = match res { - Ok(encoder) => (encoder, None), - Err(e) => ( - wgpu_core::command::RenderBundleEncoder::dummy(self.id), - Some(e), - ), - }; - - self.error_handler.push_error(err); - - GPURenderBundleEncoder { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - encoder: RefCell::new(Some(encoder)), - label: descriptor.label, - } - } - - #[required(1)] - #[cppgc] - fn create_query_set( - &self, - #[webidl] descriptor: crate::query_set::GPUQuerySetDescriptor, - ) -> GPUQuerySet { - let wgpu_descriptor = wgpu_core::resource::QuerySetDescriptor { - label: crate::transform_label(descriptor.label.clone()), - ty: descriptor.r#type.clone().into(), - count: descriptor.count, - }; - - let (id, err) = self - .instance - .device_create_query_set(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - GPUQuerySet { - instance: self.instance.clone(), - id, - r#type: descriptor.r#type, - count: descriptor.count, - label: descriptor.label, - } - } - - #[getter] - #[global] - fn lost(&self) -> v8::Global { - self.lost_promise.clone() - } - - #[required(1)] - fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) { - self.error_handler - .scopes - .lock() - .unwrap() - .push((filter, vec![])); - } - - #[async_method(fake)] - #[global] - fn pop_error_scope( - &self, - scope: &mut v8::HandleScope, - ) -> Result, JsErrorBox> { - if self.error_handler.is_lost.get().is_some() { - let val = v8::null(scope).cast::(); - return Ok(v8::Global::new(scope, val)); + } + GPUBindingResource::BufferBinding(buffer_binding) => { + BindingResource::Buffer(wgpu_core::binding_model::BufferBinding { + buffer: buffer_binding.buffer.id, + offset: buffer_binding.offset, + size: buffer_binding.size.and_then(NonZeroU64::new), + }) + } + }, + }) + .collect::>(); + + let wgpu_descriptor = wgpu_core::binding_model::BindGroupDescriptor { + label: crate::transform_label(descriptor.label.clone()), + layout: descriptor.layout.id, + entries: Cow::Owned(entries), + }; + + let (id, err) = + self + .instance + .device_create_bind_group(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + GPUBindGroup { + instance: self.instance.clone(), + id, + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_shader_module( + &self, + scope: &mut v8::HandleScope<'_>, + #[webidl] descriptor: super::shader::GPUShaderModuleDescriptor, + ) -> GPUShaderModule { + let wgpu_descriptor = wgpu_core::pipeline::ShaderModuleDescriptor { + label: crate::transform_label(descriptor.label.clone()), + runtime_checks: wgpu_types::ShaderRuntimeChecks::default(), + }; + + let (id, err) = self.instance.device_create_shader_module( + self.id, + &wgpu_descriptor, + wgpu_core::pipeline::ShaderModuleSource::Wgsl(Cow::Borrowed( + &descriptor.code, + )), + None, + ); + + let compilation_info = + GPUCompilationInfo::new(scope, err.iter(), &descriptor.code); + let compilation_info = make_cppgc_object(scope, compilation_info); + let compilation_info = v8::Global::new(scope, compilation_info); + self.error_handler.push_error(err); + + GPUShaderModule { + instance: self.instance.clone(), + id, + label: descriptor.label, + compilation_info, + } + } + + #[required(1)] + #[cppgc] + fn create_compute_pipeline( + &self, + #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, + ) -> GPUComputePipeline { + self.new_compute_pipeline(descriptor) + } + + #[required(1)] + #[cppgc] + fn create_render_pipeline( + &self, + #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor, + ) -> Result { + self.new_render_pipeline(descriptor) + } + + #[async_method] + #[required(1)] + #[cppgc] + async fn create_compute_pipeline_async( + &self, + #[webidl] descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, + ) -> GPUComputePipeline { + self.new_compute_pipeline(descriptor) + } + + #[async_method] + #[required(1)] + #[cppgc] + async fn create_render_pipeline_async( + &self, + #[webidl] descriptor: super::render_pipeline::GPURenderPipelineDescriptor, + ) -> Result { + self.new_render_pipeline(descriptor) + } + + #[cppgc] + fn create_command_encoder( + &self, + #[webidl] descriptor: Option< + super::command_encoder::GPUCommandEncoderDescriptor, + >, + ) -> GPUCommandEncoder { + let label = descriptor.map(|d| d.label).unwrap_or_default(); + let wgpu_descriptor = wgpu_types::CommandEncoderDescriptor { + label: Some(Cow::Owned(label.clone())), + }; + + let (id, err) = self.instance.device_create_command_encoder( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + GPUCommandEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label, + } + } + + #[required(1)] + #[cppgc] + fn create_render_bundle_encoder( + &self, + #[webidl] + descriptor: super::render_bundle::GPURenderBundleEncoderDescriptor, + ) -> GPURenderBundleEncoder { + let wgpu_descriptor = wgpu_core::command::RenderBundleEncoderDescriptor { + label: crate::transform_label(descriptor.label.clone()), + color_formats: Cow::Owned( + descriptor + .color_formats + .into_iter() + .map(|format| format.into_option().map(Into::into)) + .collect::>(), + ), + depth_stencil: descriptor.depth_stencil_format.map(|format| { + wgpu_types::RenderBundleDepthStencil { + format: format.into(), + depth_read_only: descriptor.depth_read_only, + stencil_read_only: descriptor.stencil_read_only, } - - let Some((_, errors)) = self.error_handler.scopes.lock().unwrap().pop() else { - return Err(JsErrorBox::new( - "DOMExceptionOperationError", - "There are no error scopes on the error scope stack", - )); - }; - - let val = if let Some(err) = errors.into_iter().next() { - deno_core::error::to_v8_error(scope, &err) - } else { - v8::null(scope).into() - }; - - Ok(v8::Global::new(scope, val)) - } - - #[fast] - fn start_capture(&self) { - unsafe { - self.instance - .device_start_graphics_debugger_capture(self.id) - }; - } - #[fast] - fn stop_capture(&self) { - self.instance - .device_poll(self.id, wgpu_types::PollType::wait()) - .unwrap(); - unsafe { self.instance.device_stop_graphics_debugger_capture(self.id) }; - } + }), + sample_count: descriptor.sample_count, + multiview: None, + }; + + let res = wgpu_core::command::RenderBundleEncoder::new( + &wgpu_descriptor, + self.id, + None, + ); + let (encoder, err) = match res { + Ok(encoder) => (encoder, None), + Err(e) => ( + wgpu_core::command::RenderBundleEncoder::dummy(self.id), + Some(e), + ), + }; + + self.error_handler.push_error(err); + + GPURenderBundleEncoder { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + encoder: RefCell::new(Some(encoder)), + label: descriptor.label, + } + } + + #[required(1)] + #[cppgc] + fn create_query_set( + &self, + #[webidl] descriptor: crate::query_set::GPUQuerySetDescriptor, + ) -> GPUQuerySet { + let wgpu_descriptor = wgpu_core::resource::QuerySetDescriptor { + label: crate::transform_label(descriptor.label.clone()), + ty: descriptor.r#type.clone().into(), + count: descriptor.count, + }; + + let (id, err) = + self + .instance + .device_create_query_set(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + GPUQuerySet { + instance: self.instance.clone(), + id, + r#type: descriptor.r#type, + count: descriptor.count, + label: descriptor.label, + } + } + + #[getter] + #[global] + fn lost(&self) -> v8::Global { + self.lost_promise.clone() + } + + #[required(1)] + #[undefined] + fn push_error_scope(&self, #[webidl] filter: super::error::GPUErrorFilter) { + self + .error_handler + .scopes + .lock() + .unwrap() + .push((filter, vec![])); + } + + #[async_method(fake)] + #[global] + fn pop_error_scope( + &self, + scope: &mut v8::HandleScope, + ) -> Result, JsErrorBox> { + if self.error_handler.is_lost.get().is_some() { + let val = v8::null(scope).cast::(); + return Ok(v8::Global::new(scope, val)); + } + + let Some((_, errors)) = self.error_handler.scopes.lock().unwrap().pop() + else { + return Err(JsErrorBox::new( + "DOMExceptionOperationError", + "There are no error scopes on the error scope stack", + )); + }; + + let val = if let Some(err) = errors.into_iter().next() { + deno_core::error::to_v8_error(scope, &err) + } else { + v8::null(scope).into() + }; + + Ok(v8::Global::new(scope, val)) + } + + #[fast] + fn start_capture(&self) { + unsafe { + self + .instance + .device_start_graphics_debugger_capture(self.id) + }; + } + #[fast] + fn stop_capture(&self) { + self + .instance + .device_poll(self.id, wgpu_types::PollType::wait_indefinitely()) + .unwrap(); + unsafe { self.instance.device_stop_graphics_debugger_capture(self.id) }; + } } impl GPUDevice { - fn new_compute_pipeline( - &self, - descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, - ) -> GPUComputePipeline { - let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor { - label: crate::transform_label(descriptor.label.clone()), - layout: descriptor.layout.into(), - stage: ProgrammableStageDescriptor { - module: descriptor.compute.module.id, - entry_point: descriptor.compute.entry_point.map(Into::into), - constants: descriptor.compute.constants.into_iter().collect(), - zero_initialize_workgroup_memory: true, - }, - cache: None, - }; - - let (id, err) = - self.instance - .device_create_compute_pipeline(self.id, &wgpu_descriptor, None, None); - - self.error_handler.push_error(err); - - GPUComputePipeline { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - id, - label: descriptor.label.clone(), - } - } - - fn new_render_pipeline( - &self, - descriptor: super::render_pipeline::GPURenderPipelineDescriptor, - ) -> Result { - let vertex = wgpu_core::pipeline::VertexState { - stage: ProgrammableStageDescriptor { - module: descriptor.vertex.module.id, - entry_point: descriptor.vertex.entry_point.map(Into::into), - constants: descriptor.vertex.constants.into_iter().collect(), - zero_initialize_workgroup_memory: true, - }, - buffers: Cow::Owned( - descriptor - .vertex - .buffers + fn new_compute_pipeline( + &self, + descriptor: super::compute_pipeline::GPUComputePipelineDescriptor, + ) -> GPUComputePipeline { + let wgpu_descriptor = wgpu_core::pipeline::ComputePipelineDescriptor { + label: crate::transform_label(descriptor.label.clone()), + layout: descriptor.layout.into(), + stage: ProgrammableStageDescriptor { + module: descriptor.compute.module.id, + entry_point: descriptor.compute.entry_point.map(Into::into), + constants: descriptor.compute.constants.into_iter().collect(), + zero_initialize_workgroup_memory: true, + }, + cache: None, + }; + + let (id, err) = self.instance.device_create_compute_pipeline( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + GPUComputePipeline { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label: descriptor.label.clone(), + } + } + + fn new_render_pipeline( + &self, + descriptor: super::render_pipeline::GPURenderPipelineDescriptor, + ) -> Result { + let vertex = wgpu_core::pipeline::VertexState { + stage: ProgrammableStageDescriptor { + module: descriptor.vertex.module.id, + entry_point: descriptor.vertex.entry_point.map(Into::into), + constants: descriptor.vertex.constants.into_iter().collect(), + zero_initialize_workgroup_memory: true, + }, + buffers: Cow::Owned( + descriptor + .vertex + .buffers + .into_iter() + .map(|b| { + b.into_option().map_or_else( + wgpu_core::pipeline::VertexBufferLayout::default, + |layout| wgpu_core::pipeline::VertexBufferLayout { + array_stride: layout.array_stride, + step_mode: layout.step_mode.into(), + attributes: Cow::Owned( + layout + .attributes .into_iter() - .map(|b| { - let layout = b.into_option().ok_or_else(|| { - JsErrorBox::type_error( - "Nullable GPUVertexBufferLayouts are currently not supported", - ) - })?; - - Ok(wgpu_core::pipeline::VertexBufferLayout { - array_stride: layout.array_stride, - step_mode: layout.step_mode.into(), - attributes: Cow::Owned( - layout - .attributes - .into_iter() - .map(|attr| wgpu_types::VertexAttribute { - format: attr.format.into(), - offset: attr.offset, - shader_location: attr.shader_location, - }) - .collect(), - ), - }) + .map(|attr| wgpu_types::VertexAttribute { + format: attr.format.into(), + offset: attr.offset, + shader_location: attr.shader_location, }) - .collect::>()?, - ), - }; - - let primitive = wgpu_types::PrimitiveState { - topology: descriptor.primitive.topology.into(), - strip_index_format: descriptor.primitive.strip_index_format.map(Into::into), - front_face: descriptor.primitive.front_face.into(), - cull_mode: descriptor.primitive.cull_mode.into(), - unclipped_depth: descriptor.primitive.unclipped_depth, - polygon_mode: Default::default(), - conservative: false, - }; - - let depth_stencil = descriptor.depth_stencil.map(|depth_stencil| { - let front = wgpu_types::StencilFaceState { - compare: depth_stencil.stencil_front.compare.into(), - fail_op: depth_stencil.stencil_front.fail_op.into(), - depth_fail_op: depth_stencil.stencil_front.depth_fail_op.into(), - pass_op: depth_stencil.stencil_front.pass_op.into(), - }; - let back = wgpu_types::StencilFaceState { - compare: depth_stencil.stencil_back.compare.into(), - fail_op: depth_stencil.stencil_back.fail_op.into(), - depth_fail_op: depth_stencil.stencil_back.depth_fail_op.into(), - pass_op: depth_stencil.stencil_back.pass_op.into(), - }; - - wgpu_types::DepthStencilState { - format: depth_stencil.format.into(), - depth_write_enabled: depth_stencil.depth_write_enabled.unwrap_or_default(), - depth_compare: depth_stencil - .depth_compare - .map(Into::into) - .unwrap_or(wgpu_types::CompareFunction::Never), // TODO(wgpu): should be optional here - stencil: wgpu_types::StencilState { - front, - back, - read_mask: depth_stencil.stencil_read_mask, - write_mask: depth_stencil.stencil_write_mask, - }, - bias: wgpu_types::DepthBiasState { - constant: depth_stencil.depth_bias, - slope_scale: depth_stencil.depth_bias_slope_scale, - clamp: depth_stencil.depth_bias_clamp, - }, - } - }); - - let multisample = wgpu_types::MultisampleState { - count: descriptor.multisample.count, - mask: descriptor.multisample.mask as u64, - alpha_to_coverage_enabled: descriptor.multisample.alpha_to_coverage_enabled, - }; - - let fragment = descriptor - .fragment - .map(|fragment| { - Ok::<_, JsErrorBox>(wgpu_core::pipeline::FragmentState { - stage: ProgrammableStageDescriptor { - module: fragment.module.id, - entry_point: fragment.entry_point.map(Into::into), - constants: fragment.constants.into_iter().collect(), - zero_initialize_workgroup_memory: true, - }, - targets: Cow::Owned( - fragment - .targets - .into_iter() - .map(|target| { - target - .into_option() - .map(|target| { - Ok(wgpu_types::ColorTargetState { - format: target.format.into(), - blend: target.blend.map(|blend| { - wgpu_types::BlendState { - color: wgpu_types::BlendComponent { - src_factor: blend.color.src_factor.into(), - dst_factor: blend.color.dst_factor.into(), - operation: blend.color.operation.into(), - }, - alpha: wgpu_types::BlendComponent { - src_factor: blend.alpha.src_factor.into(), - dst_factor: blend.alpha.dst_factor.into(), - operation: blend.alpha.operation.into(), - }, - } - }), - write_mask: wgpu_types::ColorWrites::from_bits( - target.write_mask, - ) - .ok_or_else(|| { - JsErrorBox::type_error("usage is not valid") - })?, - }) - }) - .transpose() - }) - .collect::>()?, - ), - }) - }) - .transpose()?; - - let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { - label: crate::transform_label(descriptor.label.clone()), - layout: descriptor.layout.into(), - vertex, - primitive, - depth_stencil, - multisample, - fragment, - cache: None, - multiview: None, - }; - - let (id, err) = - self.instance - .device_create_render_pipeline(self.id, &wgpu_descriptor, None, None); - - self.error_handler.push_error(err); - - Ok(GPURenderPipeline { - instance: self.instance.clone(), - error_handler: self.error_handler.clone(), - id, - label: descriptor.label, + .collect(), + ), + }, + ) + }) + .collect(), + ), + }; + + let primitive = wgpu_types::PrimitiveState { + topology: descriptor.primitive.topology.into(), + strip_index_format: descriptor + .primitive + .strip_index_format + .map(Into::into), + front_face: descriptor.primitive.front_face.into(), + cull_mode: descriptor.primitive.cull_mode.into(), + unclipped_depth: descriptor.primitive.unclipped_depth, + polygon_mode: Default::default(), + conservative: false, + }; + + let depth_stencil = descriptor.depth_stencil.map(|depth_stencil| { + let front = wgpu_types::StencilFaceState { + compare: depth_stencil.stencil_front.compare.into(), + fail_op: depth_stencil.stencil_front.fail_op.into(), + depth_fail_op: depth_stencil.stencil_front.depth_fail_op.into(), + pass_op: depth_stencil.stencil_front.pass_op.into(), + }; + let back = wgpu_types::StencilFaceState { + compare: depth_stencil.stencil_back.compare.into(), + fail_op: depth_stencil.stencil_back.fail_op.into(), + depth_fail_op: depth_stencil.stencil_back.depth_fail_op.into(), + pass_op: depth_stencil.stencil_back.pass_op.into(), + }; + + wgpu_types::DepthStencilState { + format: depth_stencil.format.into(), + depth_write_enabled: depth_stencil + .depth_write_enabled + .unwrap_or_default(), + depth_compare: depth_stencil + .depth_compare + .map(Into::into) + .unwrap_or(wgpu_types::CompareFunction::Never), // TODO(wgpu): should be optional here + stencil: wgpu_types::StencilState { + front, + back, + read_mask: depth_stencil.stencil_read_mask, + write_mask: depth_stencil.stencil_write_mask, + }, + bias: wgpu_types::DepthBiasState { + constant: depth_stencil.depth_bias, + slope_scale: depth_stencil.depth_bias_slope_scale, + clamp: depth_stencil.depth_bias_clamp, + }, + } + }); + + let multisample = wgpu_types::MultisampleState { + count: descriptor.multisample.count, + mask: descriptor.multisample.mask as u64, + alpha_to_coverage_enabled: descriptor + .multisample + .alpha_to_coverage_enabled, + }; + + let fragment = descriptor + .fragment + .map(|fragment| { + Ok::<_, JsErrorBox>(wgpu_core::pipeline::FragmentState { + stage: ProgrammableStageDescriptor { + module: fragment.module.id, + entry_point: fragment.entry_point.map(Into::into), + constants: fragment.constants.into_iter().collect(), + zero_initialize_workgroup_memory: true, + }, + targets: Cow::Owned( + fragment + .targets + .into_iter() + .map(|target| { + target + .into_option() + .map(|target| { + Ok(wgpu_types::ColorTargetState { + format: target.format.into(), + blend: target.blend.map(|blend| wgpu_types::BlendState { + color: wgpu_types::BlendComponent { + src_factor: blend.color.src_factor.into(), + dst_factor: blend.color.dst_factor.into(), + operation: blend.color.operation.into(), + }, + alpha: wgpu_types::BlendComponent { + src_factor: blend.alpha.src_factor.into(), + dst_factor: blend.alpha.dst_factor.into(), + operation: blend.alpha.operation.into(), + }, + }), + write_mask: wgpu_types::ColorWrites::from_bits( + target.write_mask, + ) + .ok_or_else(|| { + JsErrorBox::type_error("usage is not valid") + })?, + }) + }) + .transpose() + }) + .collect::>()?, + ), }) - } + }) + .transpose()?; + + let wgpu_descriptor = wgpu_core::pipeline::RenderPipelineDescriptor { + label: crate::transform_label(descriptor.label.clone()), + layout: descriptor.layout.into(), + vertex, + primitive, + depth_stencil, + multisample, + fragment, + cache: None, + multiview: None, + }; + + let (id, err) = self.instance.device_create_render_pipeline( + self.id, + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + Ok(GPURenderPipeline { + instance: self.instance.clone(), + error_handler: self.error_handler.clone(), + id, + label: descriptor.label, + }) + } } #[derive(Clone, Debug, Default, Hash, Eq, PartialEq)] pub enum GPUDeviceLostReason { - #[default] - Unknown, - Destroyed, + #[default] + Unknown, + Destroyed, } impl From for GPUDeviceLostReason { - fn from(value: wgpu_types::DeviceLostReason) -> Self { - match value { - wgpu_types::DeviceLostReason::Unknown => Self::Unknown, - wgpu_types::DeviceLostReason::Destroyed => Self::Destroyed, - } + fn from(value: wgpu_types::DeviceLostReason) -> Self { + match value { + wgpu_types::DeviceLostReason::Unknown => Self::Unknown, + wgpu_types::DeviceLostReason::Destroyed => Self::Destroyed, } + } } #[derive(Default)] pub struct GPUDeviceLostInfo { - pub reason: GPUDeviceLostReason, + pub reason: GPUDeviceLostReason, } -impl GarbageCollected for GPUDeviceLostInfo {} +impl GarbageCollected for GPUDeviceLostInfo { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUDeviceLostInfo" + } +} #[op2] impl GPUDeviceLostInfo { - #[getter] - #[string] - fn reason(&self) -> &'static str { - use GPUDeviceLostReason::*; - match self.reason { - Unknown => "unknown", - Destroyed => "destroyed", - } - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn reason(&self) -> &'static str { + use GPUDeviceLostReason::*; + match self.reason { + Unknown => "unknown", + Destroyed => "destroyed", + } + } + + #[getter] + #[string] + fn message(&self) -> &'static str { + "device was lost" + } +} - #[getter] - #[string] - fn message(&self) -> &'static str { - "device was lost" - } +#[op2(fast)] +pub fn op_webgpu_device_start_capture(#[cppgc] device: &GPUDevice) { + unsafe { + device + .instance + .device_start_graphics_debugger_capture(device.id); + } +} + +#[op2(fast)] +pub fn op_webgpu_device_stop_capture(#[cppgc] device: &GPUDevice) { + unsafe { + device + .instance + .device_stop_graphics_debugger_capture(device.id); + } } diff --git a/deno_webgpu/error.rs b/deno_webgpu/error.rs index 066b2a4e810..57bfd144512 100644 --- a/deno_webgpu/error.rs +++ b/deno_webgpu/error.rs @@ -44,325 +44,338 @@ use crate::device::GPUDeviceLostReason; pub type ErrorHandler = std::rc::Rc; pub struct DeviceErrorHandler { - pub is_lost: OnceLock<()>, - pub scopes: Mutex)>>, - lost_resolver: Mutex>>, - spawner: V8TaskSpawner, + pub is_lost: OnceLock<()>, + pub scopes: Mutex)>>, + lost_resolver: Mutex>>, + spawner: V8TaskSpawner, - // The error handler is constructed before the device. A weak - // reference to the device is placed here with `set_device` - // after the device is constructed. - device: OnceLock>, + // The error handler is constructed before the device. A weak + // reference to the device is placed here with `set_device` + // after the device is constructed. + device: OnceLock>, } impl DeviceErrorHandler { - pub fn new(lost_resolver: v8::Global, spawner: V8TaskSpawner) -> Self { - Self { - is_lost: Default::default(), - scopes: Mutex::new(vec![]), - lost_resolver: Mutex::new(Some(lost_resolver)), - device: OnceLock::new(), - spawner, - } - } + pub fn new( + lost_resolver: v8::Global, + spawner: V8TaskSpawner, + ) -> Self { + Self { + is_lost: Default::default(), + scopes: Mutex::new(vec![]), + lost_resolver: Mutex::new(Some(lost_resolver)), + device: OnceLock::new(), + spawner, + } + } + + pub fn set_device(&self, device: v8::Weak) { + self.device.set(device).unwrap() + } + + pub fn push_error>(&self, err: Option) { + let Some(err) = err else { + return; + }; + + if self.is_lost.get().is_some() { + return; + } + + let err = err.into(); + + if let GPUError::Lost(reason) = err { + let _ = self.is_lost.set(()); + if let Some(resolver) = self.lost_resolver.lock().unwrap().take() { + self.spawner.spawn(move |scope| { + let resolver = v8::Local::new(scope, resolver); + let info = make_cppgc_object(scope, GPUDeviceLostInfo { reason }); + let info = v8::Local::new(scope, info); + resolver.resolve(scope, info.into()); + }); + } + + return; + } + + let error_filter = match err { + GPUError::Lost(_) => unreachable!(), + GPUError::Validation(_) => GPUErrorFilter::Validation, + GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory, + GPUError::Internal => GPUErrorFilter::Internal, + }; + + let mut scopes = self.scopes.lock().unwrap(); + let scope = scopes + .iter_mut() + .rfind(|(filter, _)| filter == &error_filter); + + if let Some(scope) = scope { + scope.1.push(err); + } else { + let device = self + .device + .get() + .expect("set_device was not called") + .clone(); + self.spawner.spawn(move |scope| { + let state = JsRuntime::op_state_from(&*scope); + let Some(device) = device.to_local(scope) else { + // The device has already gone away, so we don't have + // anywhere to report the error. + return; + }; + let key = v8::String::new(scope, "dispatchEvent").unwrap(); + let val = device.get(scope, key.into()).unwrap(); + let func = + v8::Global::new(scope, val.try_cast::().unwrap()); + let device = v8::Global::new(scope, device.cast::()); + let error_event_class = + state.borrow().borrow::().0.clone(); - pub fn set_device(&self, device: v8::Weak) { - self.device.set(device).unwrap() - } + let error = deno_core::error::to_v8_error(scope, &err); - pub fn push_error>(&self, err: Option) { - let Some(err) = err else { - return; - }; + let error_event_class = + v8::Local::new(scope, error_event_class.clone()); + let constructor = + v8::Local::::try_from(error_event_class).unwrap(); + let kind = v8::String::new(scope, "uncapturederror").unwrap(); - if self.is_lost.get().is_some() { - return; - } - - let err = err.into(); - - if let GPUError::Lost(reason) = err { - let _ = self.is_lost.set(()); - if let Some(resolver) = self.lost_resolver.lock().unwrap().take() { - self.spawner.spawn(move |scope| { - let resolver = v8::Local::new(scope, resolver); - let info = make_cppgc_object(scope, GPUDeviceLostInfo { reason }); - let info = v8::Local::new(scope, info); - resolver.resolve(scope, info.into()); - }); - } - - return; - } - - let error_filter = match err { - GPUError::Lost(_) => unreachable!(), - GPUError::Validation(_) => GPUErrorFilter::Validation, - GPUError::OutOfMemory => GPUErrorFilter::OutOfMemory, - GPUError::Internal => GPUErrorFilter::Internal, - }; + let obj = v8::Object::new(scope); + let key = v8::String::new(scope, "error").unwrap(); + obj.set(scope, key.into(), error); + + let event = constructor + .new_instance(scope, &[kind.into(), obj.into()]) + .unwrap(); - let mut scopes = self.scopes.lock().unwrap(); - let scope = scopes - .iter_mut() - .rfind(|(filter, _)| filter == &error_filter); - - if let Some(scope) = scope { - scope.1.push(err); - } else { - let device = self - .device - .get() - .expect("set_device was not called") - .clone(); - self.spawner.spawn(move |scope| { - let state = JsRuntime::op_state_from(&*scope); - let Some(device) = device.to_local(scope) else { - // The device has already gone away, so we don't have - // anywhere to report the error. - return; - }; - let key = v8::String::new(scope, "dispatchEvent").unwrap(); - let val = device.get(scope, key.into()).unwrap(); - let func = v8::Global::new(scope, val.try_cast::().unwrap()); - let device = v8::Global::new(scope, device.cast::()); - let error_event_class = state.borrow().borrow::().0.clone(); - - let error = deno_core::error::to_v8_error(scope, &err); - - let error_event_class = v8::Local::new(scope, error_event_class.clone()); - let constructor = v8::Local::::try_from(error_event_class).unwrap(); - let kind = v8::String::new(scope, "uncapturederror").unwrap(); - - let obj = v8::Object::new(scope); - let key = v8::String::new(scope, "error").unwrap(); - obj.set(scope, key.into(), error); - - let event = constructor - .new_instance(scope, &[kind.into(), obj.into()]) - .unwrap(); - - let recv = v8::Local::new(scope, device); - func.open(scope).call(scope, recv, &[event.into()]); - }); - } + let recv = v8::Local::new(scope, device); + func.open(scope).call(scope, recv, &[event.into()]); + }); } + } } #[derive(deno_core::WebIDL, Eq, PartialEq)] #[webidl(enum)] pub enum GPUErrorFilter { - Validation, - OutOfMemory, - Internal, + Validation, + OutOfMemory, + Internal, } #[derive(Debug, deno_error::JsError)] pub enum GPUError { - // TODO(@crowlKats): consider adding an unreachable value that uses unreachable!() - #[class("UNREACHABLE")] - Lost(GPUDeviceLostReason), - #[class("GPUValidationError")] - Validation(String), - #[class("GPUOutOfMemoryError")] - OutOfMemory, - #[allow(dead_code)] - #[class("GPUInternalError")] - Internal, + // TODO(@crowlKats): consider adding an unreachable value that uses unreachable!() + #[class("UNREACHABLE")] + Lost(GPUDeviceLostReason), + #[class("GPUValidationError")] + Validation(String), + #[class("GPUOutOfMemoryError")] + OutOfMemory, + #[class("GPUInternalError")] + Internal, } impl Display for GPUError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match self { - GPUError::Lost(_) => Ok(()), - GPUError::Validation(s) => f.write_str(s), - GPUError::OutOfMemory => f.write_str("not enough memory left"), - GPUError::Internal => Ok(()), - } + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + GPUError::Lost(_) => Ok(()), + GPUError::Validation(s) => f.write_str(s), + GPUError::OutOfMemory => f.write_str("not enough memory left"), + GPUError::Internal => Ok(()), } + } } impl std::error::Error for GPUError {} impl GPUError { - fn from_webgpu(e: impl WebGpuError) -> Self { - match e.webgpu_error_type() { - ErrorType::Internal => GPUError::Internal, - ErrorType::DeviceLost => GPUError::Lost(GPUDeviceLostReason::Unknown), // TODO: this variant should be ignored, register the lost callback instead. - ErrorType::OutOfMemory => GPUError::OutOfMemory, - ErrorType::Validation => GPUError::Validation(fmt_err(&e)), - } + fn from_webgpu(e: impl WebGpuError) -> Self { + match e.webgpu_error_type() { + ErrorType::Internal => GPUError::Internal, + ErrorType::DeviceLost => GPUError::Lost(GPUDeviceLostReason::Unknown), // TODO: this variant should be ignored, register the lost callback instead. + ErrorType::OutOfMemory => GPUError::OutOfMemory, + ErrorType::Validation => GPUError::Validation(fmt_err(&e)), } + } } fn fmt_err(err: &(dyn std::error::Error + 'static)) -> String { - let mut output = err.to_string(); + let mut output = err.to_string(); - let mut e = err.source(); - while let Some(source) = e { - output.push_str(&format!(": {source}")); - e = source.source(); - } + let mut e = err.source(); + while let Some(source) = e { + output.push_str(&format!(": {source}")); + e = source.source(); + } - if output.is_empty() { - output.push_str("validation error"); - } + if output.is_empty() { + output.push_str("validation error"); + } - output + output } impl From for GPUError { - fn from(err: EncoderStateError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: EncoderStateError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: PassStateError) -> Self { - GPUError::Validation(fmt_err(&err)) - } + fn from(err: PassStateError) -> Self { + GPUError::Validation(fmt_err(&err)) + } } impl From for GPUError { - fn from(err: CreateBufferError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateBufferError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: DeviceError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: DeviceError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: BufferAccessError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: BufferAccessError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateBindGroupLayoutError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateBindGroupLayoutError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreatePipelineLayoutError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreatePipelineLayoutError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateBindGroupError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateBindGroupError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: RenderBundleError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: RenderBundleError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateRenderBundleError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateRenderBundleError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CommandEncoderError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CommandEncoderError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: QueryError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: QueryError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: ComputePassError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: ComputePassError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateComputePipelineError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateComputePipelineError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: GetBindGroupLayoutError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: GetBindGroupLayoutError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateRenderPipelineError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateRenderPipelineError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: RenderPassError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: RenderPassError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateSamplerError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateSamplerError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateShaderModuleError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateShaderModuleError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateTextureError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateTextureError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateTextureViewError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateTextureViewError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: CreateQuerySetError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: CreateQuerySetError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: QueueSubmitError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: QueueSubmitError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: QueueWriteError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: QueueWriteError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: ClearError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: ClearError) -> Self { + GPUError::from_webgpu(err) + } } impl From for GPUError { - fn from(err: ConfigureSurfaceError) -> Self { - GPUError::from_webgpu(err) - } + fn from(err: ConfigureSurfaceError) -> Self { + GPUError::from_webgpu(err) + } +} + +#[derive(Debug, thiserror::Error, deno_error::JsError)] +pub enum GPUGenericError { + #[class(type)] + #[error("Illegal constructor")] + InvalidConstructor, } diff --git a/deno_webgpu/lib.rs b/deno_webgpu/lib.rs index 6ce31a29c52..2b6dfcfee4e 100644 --- a/deno_webgpu/lib.rs +++ b/deno_webgpu/lib.rs @@ -15,6 +15,8 @@ pub use wgpu_core; pub use wgpu_types; use wgpu_types::PowerPreference; +use crate::error::GPUGenericError; + mod adapter; mod bind_group; mod bind_group_layout; @@ -42,170 +44,185 @@ pub const UNSTABLE_FEATURE_NAME: &str = "webgpu"; #[allow(clippy::print_stdout)] pub fn print_linker_flags(name: &str) { - if cfg!(windows) { - // these dls load slowly, so delay loading them - let dlls = [ - // webgpu - "d3dcompiler_47", - "OPENGL32", - // network related functions - "iphlpapi", - ]; - for dll in dlls { - println!("cargo:rustc-link-arg-bin={name}=/delayload:{dll}.dll"); - } - // enable delay loading - println!("cargo:rustc-link-arg-bin={name}=delayimp.lib"); + if cfg!(windows) { + // these dls load slowly, so delay loading them + let dlls = [ + // webgpu + "d3dcompiler_47", + "OPENGL32", + // network related functions + "iphlpapi", + ]; + for dll in dlls { + println!("cargo:rustc-link-arg-bin={name}=/delayload:{dll}.dll"); } + // enable delay loading + println!("cargo:rustc-link-arg-bin={name}=delayimp.lib"); + } } pub type Instance = Arc; deno_core::extension!( - deno_webgpu, - deps = [deno_webidl, deno_web], - ops = [op_create_gpu], - objects = [ - GPU, - adapter::GPUAdapter, - adapter::GPUAdapterInfo, - bind_group::GPUBindGroup, - bind_group_layout::GPUBindGroupLayout, - buffer::GPUBuffer, - command_buffer::GPUCommandBuffer, - command_encoder::GPUCommandEncoder, - compute_pass::GPUComputePassEncoder, - compute_pipeline::GPUComputePipeline, - device::GPUDevice, - device::GPUDeviceLostInfo, - pipeline_layout::GPUPipelineLayout, - query_set::GPUQuerySet, - queue::GPUQueue, - render_bundle::GPURenderBundle, - render_bundle::GPURenderBundleEncoder, - render_pass::GPURenderPassEncoder, - render_pipeline::GPURenderPipeline, - sampler::GPUSampler, - shader::GPUCompilationInfo, - shader::GPUCompilationMessage, - shader::GPUShaderModule, - adapter::GPUSupportedFeatures, - adapter::GPUSupportedLimits, - texture::GPUTexture, - texture::GPUTextureView, - texture::GPUExternalTexture, - byow::UnsafeWindowSurface, - surface::GPUCanvasContext, - ], - esm = ["00_init.js", "02_surface.js"], - lazy_loaded_esm = ["01_webgpu.js"], + deno_webgpu, + deps = [deno_webidl, deno_web], + ops = [ + op_create_gpu, + device::op_webgpu_device_start_capture, + device::op_webgpu_device_stop_capture, + ], + objects = [ + GPU, + adapter::GPUAdapter, + adapter::GPUAdapterInfo, + bind_group::GPUBindGroup, + bind_group_layout::GPUBindGroupLayout, + buffer::GPUBuffer, + command_buffer::GPUCommandBuffer, + command_encoder::GPUCommandEncoder, + compute_pass::GPUComputePassEncoder, + compute_pipeline::GPUComputePipeline, + device::GPUDevice, + device::GPUDeviceLostInfo, + pipeline_layout::GPUPipelineLayout, + query_set::GPUQuerySet, + queue::GPUQueue, + render_bundle::GPURenderBundle, + render_bundle::GPURenderBundleEncoder, + render_pass::GPURenderPassEncoder, + render_pipeline::GPURenderPipeline, + sampler::GPUSampler, + shader::GPUCompilationInfo, + shader::GPUCompilationMessage, + shader::GPUShaderModule, + adapter::GPUSupportedFeatures, + adapter::GPUSupportedLimits, + texture::GPUTexture, + texture::GPUTextureView, + texture::GPUExternalTexture, + byow::UnsafeWindowSurface, + surface::GPUCanvasContext, + ], + esm = ["00_init.js", "02_surface.js"], + lazy_loaded_esm = ["01_webgpu.js"], ); #[op2] #[cppgc] pub fn op_create_gpu( - state: &mut OpState, - scope: &mut v8::HandleScope, - webidl_brand: v8::Local, - set_event_target_data: v8::Local, - error_event_class: v8::Local, + state: &mut OpState, + scope: &mut v8::HandleScope, + webidl_brand: v8::Local, + set_event_target_data: v8::Local, + error_event_class: v8::Local, ) -> GPU { - state.put(EventTargetSetup { - brand: v8::Global::new(scope, webidl_brand), - set_event_target_data: v8::Global::new(scope, set_event_target_data), - }); - state.put(ErrorEventClass(v8::Global::new(scope, error_event_class))); - GPU + state.put(EventTargetSetup { + brand: v8::Global::new(scope, webidl_brand), + set_event_target_data: v8::Global::new(scope, set_event_target_data), + }); + state.put(ErrorEventClass(v8::Global::new(scope, error_event_class))); + GPU } struct EventTargetSetup { - brand: v8::Global, - set_event_target_data: v8::Global, + brand: v8::Global, + set_event_target_data: v8::Global, } struct ErrorEventClass(v8::Global); pub struct GPU; -impl GarbageCollected for GPU {} +impl GarbageCollected for GPU { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPU" + } +} #[op2] impl GPU { - #[async_method] - #[cppgc] - async fn request_adapter( - &self, - state: Rc>, - #[webidl] options: adapter::GPURequestAdapterOptions, - ) -> Option { - let mut state = state.borrow_mut(); - - let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else( - |_| wgpu_types::Backends::all(), - |s| wgpu_types::Backends::from_comma_list(&s), - ); - let instance = if let Some(instance) = state.try_borrow::() { - instance - } else { - state.put(Arc::new(wgpu_core::global::Global::new( - "webgpu", - &wgpu_types::InstanceDescriptor { - backends, - flags: wgpu_types::InstanceFlags::from_build_config(), - memory_budget_thresholds: wgpu_types::MemoryBudgetThresholds { - for_resource_creation: Some(97), - for_device_loss: Some(99), - }, - backend_options: wgpu_types::BackendOptions { - dx12: wgpu_types::Dx12BackendOptions { - shader_compiler: wgpu_types::Dx12Compiler::Fxc, - }, - gl: wgpu_types::GlBackendOptions::default(), - noop: wgpu_types::NoopBackendOptions::default(), - }, - }, - ))); - state.borrow::() - }; - - let descriptor = wgpu_core::instance::RequestAdapterOptions { - power_preference: options - .power_preference - .map(|pp| match pp { - adapter::GPUPowerPreference::LowPower => PowerPreference::LowPower, - adapter::GPUPowerPreference::HighPerformance => { - PowerPreference::HighPerformance - } - }) - .unwrap_or_default(), - force_fallback_adapter: options.force_fallback_adapter, - compatible_surface: None, // windowless - }; - let id = instance.request_adapter(&descriptor, backends, None).ok()?; - - Some(adapter::GPUAdapter { - instance: instance.clone(), - features: SameObject::new(), - limits: SameObject::new(), - info: Rc::new(SameObject::new()), - id, + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[async_method] + #[cppgc] + async fn request_adapter( + &self, + state: Rc>, + #[webidl] options: adapter::GPURequestAdapterOptions, + ) -> Option { + let mut state = state.borrow_mut(); + + let backends = std::env::var("DENO_WEBGPU_BACKEND").map_or_else( + |_| wgpu_types::Backends::all(), + |s| wgpu_types::Backends::from_comma_list(&s), + ); + let instance = if let Some(instance) = state.try_borrow::() { + instance + } else { + state.put(Arc::new(wgpu_core::global::Global::new( + "webgpu", + &wgpu_types::InstanceDescriptor { + backends, + flags: wgpu_types::InstanceFlags::from_build_config(), + memory_budget_thresholds: wgpu_types::MemoryBudgetThresholds { + for_resource_creation: Some(97), + for_device_loss: Some(99), + }, + backend_options: wgpu_types::BackendOptions { + dx12: wgpu_types::Dx12BackendOptions { + shader_compiler: wgpu_types::Dx12Compiler::Fxc, + ..Default::default() + }, + gl: wgpu_types::GlBackendOptions::default(), + noop: wgpu_types::NoopBackendOptions::default(), + }, + }, + ))); + state.borrow::() + }; + + let descriptor = wgpu_core::instance::RequestAdapterOptions { + power_preference: options + .power_preference + .map(|pp| match pp { + adapter::GPUPowerPreference::LowPower => PowerPreference::LowPower, + adapter::GPUPowerPreference::HighPerformance => { + PowerPreference::HighPerformance + } }) + .unwrap_or_default(), + force_fallback_adapter: options.force_fallback_adapter, + compatible_surface: None, // windowless + }; + let id = instance.request_adapter(&descriptor, backends, None).ok()?; + + Some(adapter::GPUAdapter { + instance: instance.clone(), + features: SameObject::new(), + limits: SameObject::new(), + info: Rc::new(SameObject::new()), + id, + }) + } + + #[string] + fn getPreferredCanvasFormat(&self) -> &'static str { + // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47 + if cfg!(target_os = "android") { + texture::GPUTextureFormat::Rgba8unorm.as_str() + } else { + texture::GPUTextureFormat::Bgra8unorm.as_str() } - - #[string] - fn getPreferredCanvasFormat(&self) -> &'static str { - // https://github.com/mozilla/gecko-dev/blob/b75080bb8b11844d18cb5f9ac6e68a866ef8e243/dom/webgpu/Instance.h#L42-L47 - if cfg!(target_os = "android") { - texture::GPUTextureFormat::Rgba8unorm.as_str() - } else { - texture::GPUTextureFormat::Bgra8unorm.as_str() - } - } + } } fn transform_label<'a>(label: String) -> Option> { - if label.is_empty() { - None - } else { - Some(std::borrow::Cow::Owned(label)) - } + if label.is_empty() { + None + } else { + Some(std::borrow::Cow::Owned(label)) + } } diff --git a/deno_webgpu/pipeline_layout.rs b/deno_webgpu/pipeline_layout.rs index c785eea7334..1d71a9902f4 100644 --- a/deno_webgpu/pipeline_layout.rs +++ b/deno_webgpu/pipeline_layout.rs @@ -6,45 +6,57 @@ use deno_core::webidl::WebIdlInterfaceConverter; use deno_core::GarbageCollected; use deno_core::WebIDL; +use crate::error::GPUGenericError; use crate::Instance; pub struct GPUPipelineLayout { - pub instance: Instance, - pub id: wgpu_core::id::PipelineLayoutId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::PipelineLayoutId, + pub label: String, } impl Drop for GPUPipelineLayout { - fn drop(&mut self) { - self.instance.pipeline_layout_drop(self.id); - } + fn drop(&mut self) { + self.instance.pipeline_layout_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUPipelineLayout { - const NAME: &'static str = "GPUPipelineLayout"; + const NAME: &'static str = "GPUPipelineLayout"; } -impl GarbageCollected for GPUPipelineLayout {} +impl GarbageCollected for GPUPipelineLayout { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUPipelineLayout" + } +} #[op2] impl GPUPipelineLayout { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUPipelineLayoutDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub bind_group_layouts: Vec>, + pub bind_group_layouts: + Vec>, } diff --git a/deno_webgpu/query_set.rs b/deno_webgpu/query_set.rs index fd6502559f5..5b5e7dd8f16 100644 --- a/deno_webgpu/query_set.rs +++ b/deno_webgpu/query_set.rs @@ -6,85 +6,96 @@ use deno_core::GarbageCollected; use deno_core::WebIDL; use deno_error::JsErrorBox; +use crate::error::GPUGenericError; use crate::Instance; pub struct GPUQuerySet { - pub instance: Instance, - pub id: wgpu_core::id::QuerySetId, - pub r#type: GPUQueryType, - pub count: u32, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::QuerySetId, + pub r#type: GPUQueryType, + pub count: u32, + pub label: String, } impl Drop for GPUQuerySet { - fn drop(&mut self) { - self.instance.query_set_drop(self.id); - } + fn drop(&mut self) { + self.instance.query_set_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUQuerySet { - const NAME: &'static str = "GPUQuerySet"; + const NAME: &'static str = "GPUQuerySet"; } -impl GarbageCollected for GPUQuerySet {} +impl GarbageCollected for GPUQuerySet { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUQuerySet" + } +} #[op2] impl GPUQuerySet { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } - #[fast] - fn destroy(&self) -> Result<(), JsErrorBox> { - // TODO(https://github.com/gfx-rs/wgpu/issues/6495): Destroy the query - // set. Until that is supported, it is okay to do nothing here, the - // query set will be garbage collected and dropped eventually. - Ok(()) - } + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } - // Naming this `type` or `r#type` does not work. - // https://github.com/gfx-rs/wgpu/issues/7778 - #[getter] - #[string] - fn ty(&self) -> &'static str { - self.r#type.as_str() - } + #[fast] + #[undefined] + fn destroy(&self) -> Result<(), JsErrorBox> { + // TODO(https://github.com/gfx-rs/wgpu/issues/6495): Destroy the query + // set. Until that is supported, it is okay to do nothing here, the + // query set will be garbage collected and dropped eventually. + Ok(()) + } - #[getter] - fn count(&self) -> u32 { - self.count - } + #[getter] + #[string] + #[rename("type")] + fn r#type(&self) -> &'static str { + self.r#type.as_str() + } + + #[getter] + fn count(&self) -> u32 { + self.count + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUQuerySetDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub r#type: GPUQueryType, - #[options(enforce_range = true)] - pub count: u32, + pub r#type: GPUQueryType, + #[options(enforce_range = true)] + pub count: u32, } #[derive(WebIDL, Clone)] #[webidl(enum)] pub(crate) enum GPUQueryType { - Occlusion, - Timestamp, + Occlusion, + Timestamp, } impl From for wgpu_types::QueryType { - fn from(value: GPUQueryType) -> Self { - match value { - GPUQueryType::Occlusion => Self::Occlusion, - GPUQueryType::Timestamp => Self::Timestamp, - } + fn from(value: GPUQueryType) -> Self { + match value { + GPUQueryType::Occlusion => Self::Occlusion, + GPUQueryType::Timestamp => Self::Timestamp, } + } } diff --git a/deno_webgpu/queue.rs b/deno_webgpu/queue.rs index 59dbffe461b..dee47927f47 100644 --- a/deno_webgpu/queue.rs +++ b/deno_webgpu/queue.rs @@ -7,13 +7,13 @@ use std::time::Duration; use deno_core::cppgc::Ptr; use deno_core::futures::channel::oneshot; use deno_core::op2; -use deno_core::v8; use deno_core::GarbageCollected; use deno_core::WebIDL; use deno_error::JsErrorBox; use crate::buffer::GPUBuffer; use crate::command_buffer::GPUCommandBuffer; +use crate::error::GPUGenericError; use crate::texture::GPUTexture; use crate::texture::GPUTextureAspect; use crate::webidl::GPUExtent3D; @@ -21,168 +21,193 @@ use crate::webidl::GPUOrigin3D; use crate::Instance; pub struct GPUQueue { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub label: String, + pub label: String, - pub id: wgpu_core::id::QueueId, - pub device: wgpu_core::id::DeviceId, + pub id: wgpu_core::id::QueueId, + pub device: wgpu_core::id::DeviceId, } impl Drop for GPUQueue { - fn drop(&mut self) { - self.instance.queue_drop(self.id); - } + fn drop(&mut self) { + self.instance.queue_drop(self.id); + } } -impl GarbageCollected for GPUQueue {} +impl GarbageCollected for GPUQueue { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUQueue" + } +} #[op2] impl GPUQueue { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[required(1)] - fn submit( - &self, - scope: &mut v8::HandleScope, - #[webidl] command_buffers: Vec>, - ) -> Result, JsErrorBox> { - let ids = command_buffers - .into_iter() - .map(|cb| cb.id) - .collect::>(); - - let err = self.instance.queue_submit(self.id, &ids).err(); - - if let Some((_, err)) = err { - self.error_handler.push_error(Some(err)); - } - - Ok(v8::undefined(scope).into()) - } - - #[async_method] - async fn on_submitted_work_done(&self) -> Result<(), JsErrorBox> { - let (sender, receiver) = oneshot::channel::<()>(); - - let callback = Box::new(move || { - sender.send(()).unwrap(); - }); - - self.instance - .queue_on_submitted_work_done(self.id, callback); - - let done = Rc::new(RefCell::new(false)); - let done_ = done.clone(); - let device_poll_fut = async move { - while !*done.borrow() { - { - self.instance - .device_poll(self.device, wgpu_types::PollType::wait()) - .unwrap(); - } - tokio::time::sleep(Duration::from_millis(10)).await; - } - Ok::<(), JsErrorBox>(()) - }; - - let receiver_fut = async move { - receiver - .await - .map_err(|e| JsErrorBox::generic(e.to_string()))?; - let mut done = done_.borrow_mut(); - *done = true; - Ok::<(), JsErrorBox>(()) - }; - - tokio::try_join!(device_poll_fut, receiver_fut)?; - - Ok(()) - } - - #[required(3)] - fn write_buffer( - &self, - #[webidl] buffer: Ptr, - #[webidl(options(enforce_range = true))] buffer_offset: u64, - #[anybuffer] buf: &[u8], - #[webidl(default = 0, options(enforce_range = true))] data_offset: u64, - #[webidl(options(enforce_range = true))] size: Option, - ) { - let data = match size { - Some(size) => &buf[(data_offset as usize)..((data_offset + size) as usize)], - None => &buf[(data_offset as usize)..], - }; - - let err = self - .instance - .queue_write_buffer(self.id, buffer.id, buffer_offset, data) - .err(); - - self.error_handler.push_error(err); + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[required(1)] + #[undefined] + fn submit( + &self, + #[webidl] command_buffers: Vec>, + ) -> Result<(), JsErrorBox> { + let ids = command_buffers + .into_iter() + .map(|cb| cb.id) + .collect::>(); + + let err = self.instance.queue_submit(self.id, &ids).err(); + + if let Some((_, err)) = err { + self.error_handler.push_error(Some(err)); } - #[required(4)] - fn write_texture( - &self, - #[webidl] destination: GPUTexelCopyTextureInfo, - #[anybuffer] buf: &[u8], - #[webidl] data_layout: GPUTexelCopyBufferLayout, - #[webidl] size: GPUExtent3D, - ) { - let destination = wgpu_core::command::TexelCopyTextureInfo { - texture: destination.texture.id, - mip_level: destination.mip_level, - origin: destination.origin.into(), - aspect: destination.aspect.into(), - }; - - let data_layout = wgpu_types::TexelCopyBufferLayout { - offset: data_layout.offset, - bytes_per_row: data_layout.bytes_per_row, - rows_per_image: data_layout.rows_per_image, - }; - - let err = self + Ok(()) + } + + // In the successful case, the promise should resolve to undefined, but + // `#[undefined]` does not seem to work here. + // https://github.com/denoland/deno/issues/29603 + #[async_method] + async fn on_submitted_work_done(&self) -> Result<(), JsErrorBox> { + let (sender, receiver) = oneshot::channel::<()>(); + + let callback = Box::new(move || { + sender.send(()).unwrap(); + }); + + self + .instance + .queue_on_submitted_work_done(self.id, callback); + + let done = Rc::new(RefCell::new(false)); + let done_ = done.clone(); + let device_poll_fut = async move { + while !*done.borrow() { + { + self .instance - .queue_write_texture(self.id, &destination, buf, &data_layout, &size.into()) - .err(); - - self.error_handler.push_error(err); - } + .device_poll(self.device, wgpu_types::PollType::wait_indefinitely()) + .unwrap(); + } + tokio::time::sleep(Duration::from_millis(10)).await; + } + Ok::<(), JsErrorBox>(()) + }; + + let receiver_fut = async move { + receiver + .await + .map_err(|e| JsErrorBox::generic(e.to_string()))?; + let mut done = done_.borrow_mut(); + *done = true; + Ok::<(), JsErrorBox>(()) + }; + + tokio::try_join!(device_poll_fut, receiver_fut)?; + + Ok(()) + } + + #[required(3)] + #[undefined] + fn write_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl(options(enforce_range = true))] buffer_offset: u64, + #[anybuffer] buf: &[u8], + #[webidl(default = 0, options(enforce_range = true))] data_offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let data = match size { + Some(size) => { + &buf[(data_offset as usize)..((data_offset + size) as usize)] + } + None => &buf[(data_offset as usize)..], + }; + + let err = self + .instance + .queue_write_buffer(self.id, buffer.id, buffer_offset, data) + .err(); + + self.error_handler.push_error(err); + } + + #[required(4)] + #[undefined] + fn write_texture( + &self, + #[webidl] destination: GPUTexelCopyTextureInfo, + #[anybuffer] buf: &[u8], + #[webidl] data_layout: GPUTexelCopyBufferLayout, + #[webidl] size: GPUExtent3D, + ) { + let destination = wgpu_types::TexelCopyTextureInfo { + texture: destination.texture.id, + mip_level: destination.mip_level, + origin: destination.origin.into(), + aspect: destination.aspect.into(), + }; + + let data_layout = wgpu_types::TexelCopyBufferLayout { + offset: data_layout.offset, + bytes_per_row: data_layout.bytes_per_row, + rows_per_image: data_layout.rows_per_image, + }; + + let err = self + .instance + .queue_write_texture( + self.id, + &destination, + buf, + &data_layout, + &size.into(), + ) + .err(); + + self.error_handler.push_error(err); + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUTexelCopyTextureInfo { - pub texture: Ptr, - #[webidl(default = 0)] - #[options(enforce_range = true)] - pub mip_level: u32, - #[webidl(default = Default::default())] - pub origin: GPUOrigin3D, - #[webidl(default = GPUTextureAspect::All)] - pub aspect: GPUTextureAspect, + pub texture: Ptr, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub mip_level: u32, + #[webidl(default = Default::default())] + pub origin: GPUOrigin3D, + #[webidl(default = GPUTextureAspect::All)] + pub aspect: GPUTextureAspect, } #[derive(WebIDL)] #[webidl(dictionary)] struct GPUTexelCopyBufferLayout { - #[webidl(default = 0)] - #[options(enforce_range = true)] - offset: u64, - #[options(enforce_range = true)] - bytes_per_row: Option, - #[options(enforce_range = true)] - rows_per_image: Option, + #[webidl(default = 0)] + #[options(enforce_range = true)] + offset: u64, + #[options(enforce_range = true)] + bytes_per_row: Option, + #[options(enforce_range = true)] + rows_per_image: Option, } diff --git a/deno_webgpu/render_bundle.rs b/deno_webgpu/render_bundle.rs index de6dec87815..964d72413f7 100644 --- a/deno_webgpu/render_bundle.rs +++ b/deno_webgpu/render_bundle.rs @@ -17,393 +17,447 @@ use deno_core::WebIDL; use deno_error::JsErrorBox; use crate::buffer::GPUBuffer; +use crate::error::GPUGenericError; use crate::texture::GPUTextureFormat; use crate::Instance; +fn c_string_truncated_at_first_nul>>( + src: T, +) -> std::ffi::CString { + std::ffi::CString::new(src).unwrap_or_else(|err| { + let nul_pos = err.nul_position(); + std::ffi::CString::new(err.into_vec().split_at(nul_pos).0).unwrap() + }) +} + pub struct GPURenderBundleEncoder { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub encoder: RefCell>, - pub label: String, + pub encoder: RefCell>, + pub label: String, } -impl GarbageCollected for GPURenderBundleEncoder {} +impl GarbageCollected for GPURenderBundleEncoder { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPURenderBundleEncoder" + } +} #[op2] impl GPURenderBundleEncoder { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[cppgc] - fn finish(&self, #[webidl] descriptor: GPURenderBundleDescriptor) -> GPURenderBundle { - let wgpu_descriptor = wgpu_core::command::RenderBundleDescriptor { - label: crate::transform_label(descriptor.label.clone()), - }; - - let (id, err) = self.instance.render_bundle_encoder_finish( - self.encoder.borrow_mut().take().unwrap(), - &wgpu_descriptor, - None, - ); - - self.error_handler.push_error(err); - - GPURenderBundle { - instance: self.instance.clone(), - id, - label: descriptor.label.clone(), - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[cppgc] + fn finish( + &self, + #[webidl] descriptor: GPURenderBundleDescriptor, + ) -> GPURenderBundle { + let wgpu_descriptor = wgpu_core::command::RenderBundleDescriptor { + label: crate::transform_label(descriptor.label.clone()), + }; + + let (id, err) = self.instance.render_bundle_encoder_finish( + self.encoder.borrow_mut().take().unwrap(), + &wgpu_descriptor, + None, + ); + + self.error_handler.push_error(err); + + GPURenderBundle { + instance: self.instance.clone(), + id, + label: descriptor.label.clone(), } - - fn push_debug_group(&self, #[webidl] group_label: String) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - let label = std::ffi::CString::new(group_label).unwrap(); - // SAFETY: the string the raw pointer points to lives longer than the below - // function invocation. - unsafe { - wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group( - encoder, - label.as_ptr(), - ); - } - - Ok(()) + } + + #[undefined] + fn push_debug_group( + &self, + #[webidl] group_label: String, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + let label = c_string_truncated_at_first_nul(group_label); + // SAFETY: the string the raw pointer points to lives longer than the below + // function invocation. + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_push_debug_group( + encoder, + label.as_ptr(), + ); } - #[fast] - fn pop_debug_group(&self) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(encoder); - Ok(()) + Ok(()) + } + + #[fast] + #[undefined] + fn pop_debug_group(&self) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + wgpu_core::command::bundle_ffi::wgpu_render_bundle_pop_debug_group(encoder); + Ok(()) + } + + #[undefined] + fn insert_debug_marker( + &self, + #[webidl] marker_label: String, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + let label = c_string_truncated_at_first_nul(marker_label); + // SAFETY: the string the raw pointer points to lives longer than the below + // function invocation. + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker( + encoder, + label.as_ptr(), + ); } - - fn insert_debug_marker(&self, #[webidl] marker_label: String) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - let label = std::ffi::CString::new(marker_label).unwrap(); - - // SAFETY: the string the raw pointer points to lives longer than the below - // function invocation. - unsafe { - wgpu_core::command::bundle_ffi::wgpu_render_bundle_insert_debug_marker( - encoder, - label.as_ptr(), - ); - } - Ok(()) - } - - fn set_bind_group<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - #[webidl(options(enforce_range = true))] index: u32, - #[webidl] bind_group: Nullable>, - dynamic_offsets: v8::Local<'a, v8::Value>, - dynamic_offsets_data_start: v8::Local<'a, v8::Value>, - dynamic_offsets_data_length: v8::Local<'a, v8::Value>, - ) -> Result<(), SetBindGroupError> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; - if let Ok(uint_32) = dynamic_offsets.try_cast::() { - let start = u64::convert( - scope, - dynamic_offsets_data_start, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 4")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? as usize; - let len = u32::convert( - scope, - dynamic_offsets_data_length, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 5")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? as usize; - - let ab = uint_32.buffer(scope).unwrap(); - let ptr = ab.data().unwrap(); - let ab_len = ab.byte_length() / 4; - - // SAFETY: created from an array buffer, slice is dropped at end of function call - let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; - - let offsets = &data[start..(start + len)]; - - // SAFETY: wgpu FFI call - unsafe { - wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( - encoder, - index, - bind_group.into_option().map(|bind_group| bind_group.id), - offsets.as_ptr(), - offsets.len(), - ); - } - } else { - let offsets = >>::convert( - scope, - dynamic_offsets, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 3")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? - .unwrap_or_default(); - - // SAFETY: wgpu FFI call - unsafe { - wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( - encoder, - index, - bind_group.into_option().map(|bind_group| bind_group.id), - offsets.as_ptr(), - offsets.len(), - ); - } - } - - Ok(()) - } - - fn set_pipeline( - &self, - #[webidl] pipeline: Ptr, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline(encoder, pipeline.id); - Ok(()) - } - - #[required(2)] - fn set_index_buffer( - &self, - #[webidl] buffer: Ptr, - #[webidl] index_format: crate::render_pipeline::GPUIndexFormat, - #[webidl(default = 0, options(enforce_range = true))] offset: u64, - #[webidl(options(enforce_range = true))] size: Option, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - encoder.set_index_buffer( - buffer.id, - index_format.into(), - offset, - size.and_then(NonZeroU64::new), + Ok(()) + } + + #[undefined] + fn set_bind_group<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl(options(enforce_range = true))] index: u32, + #[webidl] bind_group: Nullable>, + dynamic_offsets: v8::Local<'a, v8::Value>, + dynamic_offsets_data_start: v8::Local<'a, v8::Value>, + dynamic_offsets_data_length: v8::Local<'a, v8::Value>, + ) -> Result<(), SetBindGroupError> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + const PREFIX: &str = + "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; + if let Ok(uint_32) = dynamic_offsets.try_cast::() { + let start = u64::convert( + scope, + dynamic_offsets_data_start, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 4")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + let len = u32::convert( + scope, + dynamic_offsets_data_length, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 5")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + + let ab = uint_32.buffer(scope).unwrap(); + let ptr = ab.data().unwrap(); + let ab_len = ab.byte_length() / 4; + + // SAFETY: created from an array buffer, slice is dropped at end of function call + let data = + unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; + + let offsets = &data[start..(start + len)]; + + // SAFETY: wgpu FFI call + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( + encoder, + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets.as_ptr(), + offsets.len(), ); - Ok(()) - } - - #[required(2)] - fn set_vertex_buffer( - &self, - #[webidl(options(enforce_range = true))] slot: u32, - #[webidl] buffer: Ptr, // TODO(wgpu): support nullable buffer - #[webidl(default = 0, options(enforce_range = true))] offset: u64, - #[webidl(options(enforce_range = true))] size: Option, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer( - encoder, - slot, - buffer.id, - offset, - size.and_then(NonZeroU64::new), - ); - Ok(()) - } - - #[required(1)] - fn draw( - &self, - #[webidl(options(enforce_range = true))] vertex_count: u32, - #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, - #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32, - #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw( - encoder, - vertex_count, - instance_count, - first_vertex, - first_instance, + } + } else { + let offsets = >>::convert( + scope, + dynamic_offsets, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 3")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? + .unwrap_or_default(); + + // SAFETY: wgpu FFI call + unsafe { + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_bind_group( + encoder, + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets.as_ptr(), + offsets.len(), ); - Ok(()) + } } - #[required(1)] - fn draw_indexed( - &self, - #[webidl(options(enforce_range = true))] index_count: u32, - #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, - #[webidl(default = 0, options(enforce_range = true))] first_index: u32, - #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32, - #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed( - encoder, - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ); - Ok(()) - } - - #[required(2)] - fn draw_indirect( - &self, - #[webidl] indirect_buffer: Ptr, - #[webidl(options(enforce_range = true))] indirect_offset: u64, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect( - encoder, - indirect_buffer.id, - indirect_offset, - ); - Ok(()) - } - - #[required(2)] - fn draw_indexed_indirect( - &self, - #[webidl] indirect_buffer: Ptr, - #[webidl(options(enforce_range = true))] indirect_offset: u64, - ) -> Result<(), JsErrorBox> { - let mut encoder = self.encoder.borrow_mut(); - let encoder = encoder - .as_mut() - .ok_or_else(|| JsErrorBox::generic("Encoder has already been finished"))?; - - wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed_indirect( - encoder, - indirect_buffer.id, - indirect_offset, - ); - Ok(()) - } + Ok(()) + } + + #[undefined] + fn set_pipeline( + &self, + #[webidl] pipeline: Ptr, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_pipeline( + encoder, + pipeline.id, + ); + Ok(()) + } + + #[required(2)] + #[undefined] + fn set_index_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl] index_format: crate::render_pipeline::GPUIndexFormat, + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + encoder.set_index_buffer( + buffer.id, + index_format.into(), + offset, + size.and_then(NonZeroU64::new), + ); + Ok(()) + } + + #[required(2)] + #[undefined] + fn set_vertex_buffer( + &self, + #[webidl(options(enforce_range = true))] slot: u32, + #[webidl] buffer: Ptr, // TODO(wgpu): support nullable buffer + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_set_vertex_buffer( + encoder, + slot, + buffer.id, + offset, + size.and_then(NonZeroU64::new), + ); + Ok(()) + } + + #[required(1)] + #[undefined] + fn draw( + &self, + #[webidl(options(enforce_range = true))] vertex_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw( + encoder, + vertex_count, + instance_count, + first_vertex, + first_instance, + ); + Ok(()) + } + + #[required(1)] + #[undefined] + fn draw_indexed( + &self, + #[webidl(options(enforce_range = true))] index_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_index: u32, + #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed( + encoder, + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ); + Ok(()) + } + + #[required(2)] + #[undefined] + fn draw_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indirect( + encoder, + indirect_buffer.id, + indirect_offset, + ); + Ok(()) + } + + #[required(2)] + #[undefined] + fn draw_indexed_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) -> Result<(), JsErrorBox> { + let mut encoder = self.encoder.borrow_mut(); + let encoder = encoder.as_mut().ok_or_else(|| { + JsErrorBox::generic("Encoder has already been finished") + })?; + + wgpu_core::command::bundle_ffi::wgpu_render_bundle_draw_indexed_indirect( + encoder, + indirect_buffer.id, + indirect_offset, + ); + Ok(()) + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderBundleEncoderDescriptor { - #[webidl(default = String::new())] - pub label: String, - - pub color_formats: Vec>, - pub depth_stencil_format: Option, - #[webidl(default = 1)] - #[options(enforce_range = true)] - pub sample_count: u32, - - #[webidl(default = false)] - pub depth_read_only: bool, - #[webidl(default = false)] - pub stencil_read_only: bool, + #[webidl(default = String::new())] + pub label: String, + + pub color_formats: Vec>, + pub depth_stencil_format: Option, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub sample_count: u32, + + #[webidl(default = false)] + pub depth_read_only: bool, + #[webidl(default = false)] + pub stencil_read_only: bool, } #[derive(Debug, thiserror::Error, deno_error::JsError)] enum SetBindGroupError { - #[class(inherit)] - #[error(transparent)] - WebIDL(#[from] WebIdlError), - #[class(inherit)] - #[error(transparent)] - Other(#[from] JsErrorBox), + #[class(inherit)] + #[error(transparent)] + WebIDL(#[from] WebIdlError), + #[class(inherit)] + #[error(transparent)] + Other(#[from] JsErrorBox), } pub struct GPURenderBundle { - pub instance: Instance, - pub id: wgpu_core::id::RenderBundleId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::RenderBundleId, + pub label: String, } impl Drop for GPURenderBundle { - fn drop(&mut self) { - self.instance.render_bundle_drop(self.id); - } + fn drop(&mut self) { + self.instance.render_bundle_drop(self.id); + } } impl WebIdlInterfaceConverter for GPURenderBundle { - const NAME: &'static str = "GPURenderBundle"; + const NAME: &'static str = "GPURenderBundle"; } -impl GarbageCollected for GPURenderBundle {} +impl GarbageCollected for GPURenderBundle { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPURenderBundle" + } +} #[op2] impl GPURenderBundle { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderBundleDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, } diff --git a/deno_webgpu/render_pass.rs b/deno_webgpu/render_pass.rs index 97f1bd8e860..61236edcd29 100644 --- a/deno_webgpu/render_pass.rs +++ b/deno_webgpu/render_pass.rs @@ -7,6 +7,10 @@ use std::num::NonZeroU64; use deno_core::cppgc::Ptr; use deno_core::op2; use deno_core::v8; +use deno_core::v8::HandleScope; +use deno_core::v8::Local; +use deno_core::v8::Value; +use deno_core::webidl::ContextFn; use deno_core::webidl::IntOptions; use deno_core::webidl::Nullable; use deno_core::webidl::WebIdlConverter; @@ -15,462 +19,572 @@ use deno_core::GarbageCollected; use deno_core::WebIDL; use crate::buffer::GPUBuffer; +use crate::error::GPUGenericError; use crate::render_bundle::GPURenderBundle; +use crate::texture::GPUTexture; use crate::texture::GPUTextureView; use crate::webidl::GPUColor; use crate::Instance; pub struct GPURenderPassEncoder { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub render_pass: RefCell, - pub label: String, + pub render_pass: RefCell, + pub label: String, } -impl GarbageCollected for GPURenderPassEncoder {} +impl GarbageCollected for GPURenderPassEncoder { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPURenderPassEncoder" + } +} #[op2] impl GPURenderPassEncoder { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[required(6)] - fn set_viewport( - &self, - #[webidl] x: f32, - #[webidl] y: f32, - #[webidl] width: f32, - #[webidl] height: f32, - #[webidl] min_depth: f32, - #[webidl] max_depth: f32, - ) { - let err = self - .instance - .render_pass_set_viewport( - &mut self.render_pass.borrow_mut(), - x, - y, - width, - height, - min_depth, - max_depth, - ) - .err(); - self.error_handler.push_error(err); - } - - #[required(4)] - fn set_scissor_rect( - &self, - #[webidl(options(enforce_range = true))] x: u32, - #[webidl(options(enforce_range = true))] y: u32, - #[webidl(options(enforce_range = true))] width: u32, - #[webidl(options(enforce_range = true))] height: u32, - ) { - let err = self - .instance - .render_pass_set_scissor_rect(&mut self.render_pass.borrow_mut(), x, y, width, height) - .err(); - self.error_handler.push_error(err); - } - - #[required(1)] - fn set_blend_constant(&self, #[webidl] color: GPUColor) { - let err = self - .instance - .render_pass_set_blend_constant(&mut self.render_pass.borrow_mut(), color.into()) - .err(); - self.error_handler.push_error(err); - } - - #[required(1)] - fn set_stencil_reference(&self, #[webidl(options(enforce_range = true))] reference: u32) { - let err = self - .instance - .render_pass_set_stencil_reference(&mut self.render_pass.borrow_mut(), reference) - .err(); - self.error_handler.push_error(err); - } - - #[required(1)] - fn begin_occlusion_query(&self, #[webidl(options(enforce_range = true))] query_index: u32) { - let err = self - .instance - .render_pass_begin_occlusion_query(&mut self.render_pass.borrow_mut(), query_index) - .err(); - self.error_handler.push_error(err); - } - - #[fast] - fn end_occlusion_query(&self) { - let err = self - .instance - .render_pass_end_occlusion_query(&mut self.render_pass.borrow_mut()) - .err(); - self.error_handler.push_error(err); - } - - #[required(1)] - fn execute_bundles(&self, #[webidl] bundles: Vec>) { - let err = self - .instance - .render_pass_execute_bundles( - &mut self.render_pass.borrow_mut(), - &bundles - .into_iter() - .map(|bundle| bundle.id) - .collect::>(), - ) - .err(); - self.error_handler.push_error(err); - } - - #[fast] - fn end(&self) { - let err = self - .instance - .render_pass_end(&mut self.render_pass.borrow_mut()) - .err(); - self.error_handler.push_error(err); - } - - fn push_debug_group(&self, #[webidl] group_label: String) { - let err = self - .instance - .render_pass_push_debug_group( - &mut self.render_pass.borrow_mut(), - &group_label, - 0, // wgpu#975 - ) - .err(); - self.error_handler.push_error(err); - } - - #[fast] - fn pop_debug_group(&self) { - let err = self - .instance - .render_pass_pop_debug_group(&mut self.render_pass.borrow_mut()) - .err(); - self.error_handler.push_error(err); - } - - fn insert_debug_marker(&self, #[webidl] marker_label: String) { - let err = self - .instance - .render_pass_insert_debug_marker( - &mut self.render_pass.borrow_mut(), - &marker_label, - 0, // wgpu#975 - ) - .err(); - self.error_handler.push_error(err); - } - - fn set_bind_group<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - #[webidl(options(enforce_range = true))] index: u32, - #[webidl] bind_group: Nullable>, - dynamic_offsets: v8::Local<'a, v8::Value>, - dynamic_offsets_data_start: v8::Local<'a, v8::Value>, - dynamic_offsets_data_length: v8::Local<'a, v8::Value>, - ) -> Result<(), WebIdlError> { - const PREFIX: &str = "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; - - let err = if let Ok(uint_32) = dynamic_offsets.try_cast::() { - let start = u64::convert( - scope, - dynamic_offsets_data_start, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 4")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? as usize; - let len = u32::convert( - scope, - dynamic_offsets_data_length, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 5")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? as usize; - - let ab = uint_32.buffer(scope).unwrap(); - let ptr = ab.data().unwrap(); - let ab_len = ab.byte_length() / 4; - - // SAFETY: created from an array buffer, slice is dropped at end of function call - let data = unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; - - let offsets = &data[start..(start + len)]; - - self.instance - .render_pass_set_bind_group( - &mut self.render_pass.borrow_mut(), - index, - bind_group.into_option().map(|bind_group| bind_group.id), - offsets, - ) - .err() - } else { - let offsets = >>::convert( - scope, - dynamic_offsets, - Cow::Borrowed(PREFIX), - (|| Cow::Borrowed("Argument 3")).into(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )? - .unwrap_or_default(); - - self.instance - .render_pass_set_bind_group( - &mut self.render_pass.borrow_mut(), - index, - bind_group.into_option().map(|bind_group| bind_group.id), - &offsets, - ) - .err() - }; - - self.error_handler.push_error(err); - - Ok(()) - } - - fn set_pipeline(&self, #[webidl] pipeline: Ptr) { - let err = self - .instance - .render_pass_set_pipeline(&mut self.render_pass.borrow_mut(), pipeline.id) - .err(); - self.error_handler.push_error(err); - } - - #[required(2)] - fn set_index_buffer( - &self, - #[webidl] buffer: Ptr, - #[webidl] index_format: crate::render_pipeline::GPUIndexFormat, - #[webidl(default = 0, options(enforce_range = true))] offset: u64, - #[webidl(options(enforce_range = true))] size: Option, - ) { - let err = self - .instance - .render_pass_set_index_buffer( - &mut self.render_pass.borrow_mut(), - buffer.id, - index_format.into(), - offset, - size.and_then(NonZeroU64::new), - ) - .err(); - self.error_handler.push_error(err); - } - - #[required(2)] - fn set_vertex_buffer( - &self, - #[webidl(options(enforce_range = true))] slot: u32, - #[webidl] buffer: Ptr, // TODO(wgpu): support nullable buffer - #[webidl(default = 0, options(enforce_range = true))] offset: u64, - #[webidl(options(enforce_range = true))] size: Option, - ) { - let err = self - .instance - .render_pass_set_vertex_buffer( - &mut self.render_pass.borrow_mut(), - slot, - buffer.id, - offset, - size.and_then(NonZeroU64::new), - ) - .err(); - self.error_handler.push_error(err); - } - - #[required(1)] - fn draw( - &self, - #[webidl(options(enforce_range = true))] vertex_count: u32, - #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, - #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32, - #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, - ) { - let err = self - .instance - .render_pass_draw( - &mut self.render_pass.borrow_mut(), - vertex_count, - instance_count, - first_vertex, - first_instance, - ) - .err(); - self.error_handler.push_error(err); - } - - #[required(1)] - fn draw_indexed( - &self, - #[webidl(options(enforce_range = true))] index_count: u32, - #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, - #[webidl(default = 0, options(enforce_range = true))] first_index: u32, - #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32, - #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, - ) { - let err = self - .instance - .render_pass_draw_indexed( - &mut self.render_pass.borrow_mut(), - index_count, - instance_count, - first_index, - base_vertex, - first_instance, - ) - .err(); - self.error_handler.push_error(err); - } - - #[required(2)] - fn draw_indirect( - &self, - #[webidl] indirect_buffer: Ptr, - #[webidl(options(enforce_range = true))] indirect_offset: u64, - ) { - let err = self - .instance - .render_pass_draw_indirect( - &mut self.render_pass.borrow_mut(), - indirect_buffer.id, - indirect_offset, - ) - .err(); - self.error_handler.push_error(err); - } - - #[required(2)] - fn draw_indexed_indirect( - &self, - #[webidl] indirect_buffer: Ptr, - #[webidl(options(enforce_range = true))] indirect_offset: u64, - ) { - let err = self - .instance - .render_pass_draw_indexed_indirect( - &mut self.render_pass.borrow_mut(), - indirect_buffer.id, - indirect_offset, - ) - .err(); - self.error_handler.push_error(err); - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[required(6)] + #[undefined] + fn set_viewport( + &self, + #[webidl] x: f32, + #[webidl] y: f32, + #[webidl] width: f32, + #[webidl] height: f32, + #[webidl] min_depth: f32, + #[webidl] max_depth: f32, + ) { + let err = self + .instance + .render_pass_set_viewport( + &mut self.render_pass.borrow_mut(), + x, + y, + width, + height, + min_depth, + max_depth, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(4)] + #[undefined] + fn set_scissor_rect( + &self, + #[webidl(options(enforce_range = true))] x: u32, + #[webidl(options(enforce_range = true))] y: u32, + #[webidl(options(enforce_range = true))] width: u32, + #[webidl(options(enforce_range = true))] height: u32, + ) { + let err = self + .instance + .render_pass_set_scissor_rect( + &mut self.render_pass.borrow_mut(), + x, + y, + width, + height, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn set_blend_constant(&self, #[webidl] color: GPUColor) { + let err = self + .instance + .render_pass_set_blend_constant( + &mut self.render_pass.borrow_mut(), + color.into(), + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn set_stencil_reference( + &self, + #[webidl(options(enforce_range = true))] reference: u32, + ) { + let err = self + .instance + .render_pass_set_stencil_reference( + &mut self.render_pass.borrow_mut(), + reference, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn begin_occlusion_query( + &self, + #[webidl(options(enforce_range = true))] query_index: u32, + ) { + let err = self + .instance + .render_pass_begin_occlusion_query( + &mut self.render_pass.borrow_mut(), + query_index, + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + #[undefined] + fn end_occlusion_query(&self) { + let err = self + .instance + .render_pass_end_occlusion_query(&mut self.render_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn execute_bundles(&self, #[webidl] bundles: Vec>) { + let err = self + .instance + .render_pass_execute_bundles( + &mut self.render_pass.borrow_mut(), + &bundles + .into_iter() + .map(|bundle| bundle.id) + .collect::>(), + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + #[undefined] + fn end(&self) { + let err = self + .instance + .render_pass_end(&mut self.render_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn push_debug_group(&self, #[webidl] group_label: String) { + let err = self + .instance + .render_pass_push_debug_group( + &mut self.render_pass.borrow_mut(), + &group_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + #[fast] + #[undefined] + fn pop_debug_group(&self) { + let err = self + .instance + .render_pass_pop_debug_group(&mut self.render_pass.borrow_mut()) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn insert_debug_marker(&self, #[webidl] marker_label: String) { + let err = self + .instance + .render_pass_insert_debug_marker( + &mut self.render_pass.borrow_mut(), + &marker_label, + 0, // wgpu#975 + ) + .err(); + self.error_handler.push_error(err); + } + + #[undefined] + fn set_bind_group<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + #[webidl(options(enforce_range = true))] index: u32, + #[webidl] bind_group: Nullable>, + dynamic_offsets: v8::Local<'a, v8::Value>, + dynamic_offsets_data_start: v8::Local<'a, v8::Value>, + dynamic_offsets_data_length: v8::Local<'a, v8::Value>, + ) -> Result<(), WebIdlError> { + const PREFIX: &str = + "Failed to execute 'setBindGroup' on 'GPUComputePassEncoder'"; + + let err = if let Ok(uint_32) = dynamic_offsets.try_cast::() + { + let start = u64::convert( + scope, + dynamic_offsets_data_start, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 4")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + let len = u32::convert( + scope, + dynamic_offsets_data_length, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 5")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? as usize; + + let ab = uint_32.buffer(scope).unwrap(); + let ptr = ab.data().unwrap(); + let ab_len = ab.byte_length() / 4; + + // SAFETY: created from an array buffer, slice is dropped at end of function call + let data = + unsafe { std::slice::from_raw_parts(ptr.as_ptr() as _, ab_len) }; + + let offsets = &data[start..(start + len)]; + + self + .instance + .render_pass_set_bind_group( + &mut self.render_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + offsets, + ) + .err() + } else { + let offsets = >>::convert( + scope, + dynamic_offsets, + Cow::Borrowed(PREFIX), + (|| Cow::Borrowed("Argument 3")).into(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )? + .unwrap_or_default(); + + self + .instance + .render_pass_set_bind_group( + &mut self.render_pass.borrow_mut(), + index, + bind_group.into_option().map(|bind_group| bind_group.id), + &offsets, + ) + .err() + }; + + self.error_handler.push_error(err); + + Ok(()) + } + + #[undefined] + fn set_pipeline( + &self, + #[webidl] pipeline: Ptr, + ) { + let err = self + .instance + .render_pass_set_pipeline(&mut self.render_pass.borrow_mut(), pipeline.id) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + #[undefined] + fn set_index_buffer( + &self, + #[webidl] buffer: Ptr, + #[webidl] index_format: crate::render_pipeline::GPUIndexFormat, + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let err = self + .instance + .render_pass_set_index_buffer( + &mut self.render_pass.borrow_mut(), + buffer.id, + index_format.into(), + offset, + size.and_then(NonZeroU64::new), + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + #[undefined] + fn set_vertex_buffer( + &self, + #[webidl(options(enforce_range = true))] slot: u32, + #[webidl] buffer: Ptr, // TODO(wgpu): support nullable buffer + #[webidl(default = 0, options(enforce_range = true))] offset: u64, + #[webidl(options(enforce_range = true))] size: Option, + ) { + let err = self + .instance + .render_pass_set_vertex_buffer( + &mut self.render_pass.borrow_mut(), + slot, + buffer.id, + offset, + size.and_then(NonZeroU64::new), + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn draw( + &self, + #[webidl(options(enforce_range = true))] vertex_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_vertex: u32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) { + let err = self + .instance + .render_pass_draw( + &mut self.render_pass.borrow_mut(), + vertex_count, + instance_count, + first_vertex, + first_instance, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(1)] + #[undefined] + fn draw_indexed( + &self, + #[webidl(options(enforce_range = true))] index_count: u32, + #[webidl(default = 1, options(enforce_range = true))] instance_count: u32, + #[webidl(default = 0, options(enforce_range = true))] first_index: u32, + #[webidl(default = 0, options(enforce_range = true))] base_vertex: i32, + #[webidl(default = 0, options(enforce_range = true))] first_instance: u32, + ) { + let err = self + .instance + .render_pass_draw_indexed( + &mut self.render_pass.borrow_mut(), + index_count, + instance_count, + first_index, + base_vertex, + first_instance, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + #[undefined] + fn draw_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) { + let err = self + .instance + .render_pass_draw_indirect( + &mut self.render_pass.borrow_mut(), + indirect_buffer.id, + indirect_offset, + ) + .err(); + self.error_handler.push_error(err); + } + + #[required(2)] + #[undefined] + fn draw_indexed_indirect( + &self, + #[webidl] indirect_buffer: Ptr, + #[webidl(options(enforce_range = true))] indirect_offset: u64, + ) { + let err = self + .instance + .render_pass_draw_indexed_indirect( + &mut self.render_pass.borrow_mut(), + indirect_buffer.id, + indirect_offset, + ) + .err(); + self.error_handler.push_error(err); + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderPassDescriptor { - #[webidl(default = String::new())] - pub label: String, - - pub color_attachments: Vec>, - pub depth_stencil_attachment: Option, - pub occlusion_query_set: Option>, - pub timestamp_writes: Option, - /*#[webidl(default = 50000000)] - #[options(enforce_range = true)] - pub max_draw_count: u64,*/ + #[webidl(default = String::new())] + pub label: String, + + pub color_attachments: Vec>, + pub depth_stencil_attachment: Option, + pub occlusion_query_set: Option>, + pub timestamp_writes: Option, + /*#[webidl(default = 50000000)] + #[options(enforce_range = true)] + pub max_draw_count: u64,*/ } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderPassColorAttachment { - pub view: Ptr, - #[options(enforce_range = true)] - pub depth_slice: Option, - pub resolve_target: Option>, - pub clear_value: Option, - pub load_op: GPULoadOp, - pub store_op: GPUStoreOp, + pub view: GPUTextureOrView, + #[options(enforce_range = true)] + pub depth_slice: Option, + pub resolve_target: Option, + pub clear_value: Option, + pub load_op: GPULoadOp, + pub store_op: GPUStoreOp, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPULoadOp { - Load, - Clear, + Load, + Clear, } impl GPULoadOp { - pub fn with_default_value(self, val: Option) -> wgpu_core::command::LoadOp { - match self { - GPULoadOp::Load => wgpu_core::command::LoadOp::Load, - GPULoadOp::Clear => wgpu_core::command::LoadOp::Clear(val.unwrap_or_default()), - } + pub fn with_default_value( + self, + val: Option, + ) -> wgpu_core::command::LoadOp { + match self { + GPULoadOp::Load => wgpu_core::command::LoadOp::Load, + GPULoadOp::Clear => { + wgpu_core::command::LoadOp::Clear(val.unwrap_or_default()) + } } + } - pub fn with_value(self, val: V) -> wgpu_core::command::LoadOp { - match self { - GPULoadOp::Load => wgpu_core::command::LoadOp::Load, - GPULoadOp::Clear => wgpu_core::command::LoadOp::Clear(val), - } + pub fn with_value(self, val: V) -> wgpu_core::command::LoadOp { + match self { + GPULoadOp::Load => wgpu_core::command::LoadOp::Load, + GPULoadOp::Clear => wgpu_core::command::LoadOp::Clear(val), } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUStoreOp { - Store, - Discard, + Store, + Discard, } impl From for wgpu_core::command::StoreOp { - fn from(value: GPUStoreOp) -> Self { - match value { - GPUStoreOp::Store => Self::Store, - GPUStoreOp::Discard => Self::Discard, - } + fn from(value: GPUStoreOp) -> Self { + match value { + GPUStoreOp::Store => Self::Store, + GPUStoreOp::Discard => Self::Discard, } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderPassDepthStencilAttachment { - pub view: Ptr, - pub depth_clear_value: Option, - pub depth_load_op: Option, - pub depth_store_op: Option, - #[webidl(default = false)] - pub depth_read_only: bool, - #[webidl(default = 0)] - #[options(enforce_range = true)] - pub stencil_clear_value: u32, - pub stencil_load_op: Option, - pub stencil_store_op: Option, - #[webidl(default = false)] - pub stencil_read_only: bool, + pub view: GPUTextureOrView, + pub depth_clear_value: Option, + pub depth_load_op: Option, + pub depth_store_op: Option, + #[webidl(default = false)] + pub depth_read_only: bool, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub stencil_clear_value: u32, + pub stencil_load_op: Option, + pub stencil_store_op: Option, + #[webidl(default = false)] + pub stencil_read_only: bool, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderPassTimestampWrites { - pub query_set: Ptr, - #[options(enforce_range = true)] - pub beginning_of_pass_write_index: Option, - #[options(enforce_range = true)] - pub end_of_pass_write_index: Option, + pub query_set: Ptr, + #[options(enforce_range = true)] + pub beginning_of_pass_write_index: Option, + #[options(enforce_range = true)] + pub end_of_pass_write_index: Option, +} + +pub(crate) enum GPUTextureOrView { + Texture(Ptr), + TextureView(Ptr), +} + +impl GPUTextureOrView { + pub(crate) fn to_view_id(&self) -> wgpu_core::id::TextureViewId { + match self { + Self::Texture(texture) => texture.default_view_id(), + Self::TextureView(texture_view) => texture_view.id, + } + } +} + +impl<'a> WebIdlConverter<'a> for GPUTextureOrView { + type Options = (); + + fn convert<'b>( + scope: &mut HandleScope<'a>, + value: Local<'a, Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::Texture) + .or_else(|_| { + >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + ) + .map(Self::TextureView) + }) + } } diff --git a/deno_webgpu/render_pipeline.rs b/deno_webgpu/render_pipeline.rs index fcd9d786df5..1a0fc2210b2 100644 --- a/deno_webgpu/render_pipeline.rs +++ b/deno_webgpu/render_pipeline.rs @@ -9,6 +9,7 @@ use deno_core::WebIDL; use indexmap::IndexMap; use crate::bind_group_layout::GPUBindGroupLayout; +use crate::error::GPUGenericError; use crate::sampler::GPUCompareFunction; use crate::shader::GPUShaderModule; use crate::texture::GPUTextureFormat; @@ -16,535 +17,545 @@ use crate::webidl::GPUPipelineLayoutOrGPUAutoLayoutMode; use crate::Instance; pub struct GPURenderPipeline { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, - pub id: wgpu_core::id::RenderPipelineId, - pub label: String, + pub id: wgpu_core::id::RenderPipelineId, + pub label: String, } impl Drop for GPURenderPipeline { - fn drop(&mut self) { - self.instance.render_pipeline_drop(self.id); - } + fn drop(&mut self) { + self.instance.render_pipeline_drop(self.id); + } } impl WebIdlInterfaceConverter for GPURenderPipeline { - const NAME: &'static str = "GPURenderPipeline"; + const NAME: &'static str = "GPURenderPipeline"; } -impl GarbageCollected for GPURenderPipeline {} +impl GarbageCollected for GPURenderPipeline { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPURenderPipeline" + } +} #[op2] impl GPURenderPipeline { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[cppgc] - fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout { - let (id, err) = self - .instance - .render_pipeline_get_bind_group_layout(self.id, index, None); - - self.error_handler.push_error(err); - - // TODO(wgpu): needs to add a way to retrieve the label - GPUBindGroupLayout { - instance: self.instance.clone(), - id, - label: "".to_string(), - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[cppgc] + fn get_bind_group_layout(&self, #[webidl] index: u32) -> GPUBindGroupLayout { + let (id, err) = self + .instance + .render_pipeline_get_bind_group_layout(self.id, index, None); + + self.error_handler.push_error(err); + + // TODO(wgpu): needs to add a way to retrieve the label + GPUBindGroupLayout { + instance: self.instance.clone(), + id, + label: "".to_string(), } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPURenderPipelineDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode, - pub vertex: GPUVertexState, - pub primitive: GPUPrimitiveState, - pub depth_stencil: Option, - pub multisample: GPUMultisampleState, - pub fragment: Option, + pub layout: GPUPipelineLayoutOrGPUAutoLayoutMode, + pub vertex: GPUVertexState, + pub primitive: GPUPrimitiveState, + pub depth_stencil: Option, + pub multisample: GPUMultisampleState, + pub fragment: Option, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUMultisampleState { - #[webidl(default = 1)] - #[options(enforce_range = true)] - pub count: u32, - #[webidl(default = 0xFFFFFFFF)] - #[options(enforce_range = true)] - pub mask: u32, - #[webidl(default = false)] - pub alpha_to_coverage_enabled: bool, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub count: u32, + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub mask: u32, + #[webidl(default = false)] + pub alpha_to_coverage_enabled: bool, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUDepthStencilState { - pub format: GPUTextureFormat, - pub depth_write_enabled: Option, - pub depth_compare: Option, - pub stencil_front: GPUStencilFaceState, - pub stencil_back: GPUStencilFaceState, - #[webidl(default = 0xFFFFFFFF)] - #[options(enforce_range = true)] - pub stencil_read_mask: u32, - #[webidl(default = 0xFFFFFFFF)] - #[options(enforce_range = true)] - pub stencil_write_mask: u32, - #[webidl(default = 0)] - #[options(enforce_range = true)] - pub depth_bias: i32, - #[webidl(default = 0.0)] - pub depth_bias_slope_scale: f32, - #[webidl(default = 0.0)] - pub depth_bias_clamp: f32, + pub format: GPUTextureFormat, + pub depth_write_enabled: Option, + pub depth_compare: Option, + pub stencil_front: GPUStencilFaceState, + pub stencil_back: GPUStencilFaceState, + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub stencil_read_mask: u32, + #[webidl(default = 0xFFFFFFFF)] + #[options(enforce_range = true)] + pub stencil_write_mask: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + pub depth_bias: i32, + #[webidl(default = 0.0)] + pub depth_bias_slope_scale: f32, + #[webidl(default = 0.0)] + pub depth_bias_clamp: f32, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUStencilFaceState { - #[webidl(default = GPUCompareFunction::Always)] - pub compare: GPUCompareFunction, - #[webidl(default = GPUStencilOperation::Keep)] - pub fail_op: GPUStencilOperation, - #[webidl(default = GPUStencilOperation::Keep)] - pub depth_fail_op: GPUStencilOperation, - #[webidl(default = GPUStencilOperation::Keep)] - pub pass_op: GPUStencilOperation, + #[webidl(default = GPUCompareFunction::Always)] + pub compare: GPUCompareFunction, + #[webidl(default = GPUStencilOperation::Keep)] + pub fail_op: GPUStencilOperation, + #[webidl(default = GPUStencilOperation::Keep)] + pub depth_fail_op: GPUStencilOperation, + #[webidl(default = GPUStencilOperation::Keep)] + pub pass_op: GPUStencilOperation, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUStencilOperation { - Keep, - Zero, - Replace, - Invert, - IncrementClamp, - DecrementClamp, - IncrementWrap, - DecrementWrap, + Keep, + Zero, + Replace, + Invert, + IncrementClamp, + DecrementClamp, + IncrementWrap, + DecrementWrap, } impl From for wgpu_types::StencilOperation { - fn from(value: GPUStencilOperation) -> Self { - match value { - GPUStencilOperation::Keep => Self::Keep, - GPUStencilOperation::Zero => Self::Zero, - GPUStencilOperation::Replace => Self::Replace, - GPUStencilOperation::Invert => Self::Invert, - GPUStencilOperation::IncrementClamp => Self::IncrementClamp, - GPUStencilOperation::DecrementClamp => Self::DecrementClamp, - GPUStencilOperation::IncrementWrap => Self::IncrementWrap, - GPUStencilOperation::DecrementWrap => Self::DecrementWrap, - } + fn from(value: GPUStencilOperation) -> Self { + match value { + GPUStencilOperation::Keep => Self::Keep, + GPUStencilOperation::Zero => Self::Zero, + GPUStencilOperation::Replace => Self::Replace, + GPUStencilOperation::Invert => Self::Invert, + GPUStencilOperation::IncrementClamp => Self::IncrementClamp, + GPUStencilOperation::DecrementClamp => Self::DecrementClamp, + GPUStencilOperation::IncrementWrap => Self::IncrementWrap, + GPUStencilOperation::DecrementWrap => Self::DecrementWrap, } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUVertexState { - pub module: Ptr, - pub entry_point: Option, - #[webidl(default = Default::default())] - pub constants: IndexMap, - #[webidl(default = vec![])] - pub buffers: Vec>, + pub module: Ptr, + pub entry_point: Option, + #[webidl(default = Default::default())] + pub constants: IndexMap, + #[webidl(default = vec![])] + pub buffers: Vec>, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUFragmentState { - pub module: Ptr, - pub entry_point: Option, - #[webidl(default = Default::default())] - pub constants: IndexMap, - pub targets: Vec>, + pub module: Ptr, + pub entry_point: Option, + #[webidl(default = Default::default())] + pub constants: IndexMap, + pub targets: Vec>, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUColorTargetState { - pub format: GPUTextureFormat, - pub blend: Option, - #[webidl(default = 0xF)] - #[options(enforce_range = true)] - pub write_mask: u32, + pub format: GPUTextureFormat, + pub blend: Option, + #[webidl(default = 0xF)] + #[options(enforce_range = true)] + pub write_mask: u32, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBlendState { - pub color: GPUBlendComponent, - pub alpha: GPUBlendComponent, + pub color: GPUBlendComponent, + pub alpha: GPUBlendComponent, } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUBlendComponent { - #[webidl(default = GPUBlendOperation::Add)] - pub operation: GPUBlendOperation, - #[webidl(default = GPUBlendFactor::One)] - pub src_factor: GPUBlendFactor, - #[webidl(default = GPUBlendFactor::Zero)] - pub dst_factor: GPUBlendFactor, + #[webidl(default = GPUBlendOperation::Add)] + pub operation: GPUBlendOperation, + #[webidl(default = GPUBlendFactor::One)] + pub src_factor: GPUBlendFactor, + #[webidl(default = GPUBlendFactor::Zero)] + pub dst_factor: GPUBlendFactor, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUBlendOperation { - Add, - Subtract, - ReverseSubtract, - Min, - Max, + Add, + Subtract, + ReverseSubtract, + Min, + Max, } impl From for wgpu_types::BlendOperation { - fn from(value: GPUBlendOperation) -> Self { - match value { - GPUBlendOperation::Add => Self::Add, - GPUBlendOperation::Subtract => Self::Subtract, - GPUBlendOperation::ReverseSubtract => Self::ReverseSubtract, - GPUBlendOperation::Min => Self::Min, - GPUBlendOperation::Max => Self::Max, - } + fn from(value: GPUBlendOperation) -> Self { + match value { + GPUBlendOperation::Add => Self::Add, + GPUBlendOperation::Subtract => Self::Subtract, + GPUBlendOperation::ReverseSubtract => Self::ReverseSubtract, + GPUBlendOperation::Min => Self::Min, + GPUBlendOperation::Max => Self::Max, } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUBlendFactor { - #[webidl(rename = "zero")] - Zero, - #[webidl(rename = "one")] - One, - #[webidl(rename = "src")] - Src, - #[webidl(rename = "one-minus-src")] - OneMinusSrc, - #[webidl(rename = "src-alpha")] - SrcAlpha, - #[webidl(rename = "one-minus-src-alpha")] - OneMinusSrcAlpha, - #[webidl(rename = "dst")] - Dst, - #[webidl(rename = "one-minus-dst")] - OneMinusDst, - #[webidl(rename = "dst-alpha")] - DstAlpha, - #[webidl(rename = "one-minus-dst-alpha")] - OneMinusDstAlpha, - #[webidl(rename = "src-alpha-saturated")] - SrcAlphaSaturated, - #[webidl(rename = "constant")] - Constant, - #[webidl(rename = "one-minus-constant")] - OneMinusConstant, - #[webidl(rename = "src1")] - Src1, - #[webidl(rename = "one-minus-src1")] - OneMinusSrc1, - #[webidl(rename = "src1-alpha")] - Src1Alpha, - #[webidl(rename = "one-minus-src1-alpha")] - OneMinusSrc1Alpha, + #[webidl(rename = "zero")] + Zero, + #[webidl(rename = "one")] + One, + #[webidl(rename = "src")] + Src, + #[webidl(rename = "one-minus-src")] + OneMinusSrc, + #[webidl(rename = "src-alpha")] + SrcAlpha, + #[webidl(rename = "one-minus-src-alpha")] + OneMinusSrcAlpha, + #[webidl(rename = "dst")] + Dst, + #[webidl(rename = "one-minus-dst")] + OneMinusDst, + #[webidl(rename = "dst-alpha")] + DstAlpha, + #[webidl(rename = "one-minus-dst-alpha")] + OneMinusDstAlpha, + #[webidl(rename = "src-alpha-saturated")] + SrcAlphaSaturated, + #[webidl(rename = "constant")] + Constant, + #[webidl(rename = "one-minus-constant")] + OneMinusConstant, + #[webidl(rename = "src1")] + Src1, + #[webidl(rename = "one-minus-src1")] + OneMinusSrc1, + #[webidl(rename = "src1-alpha")] + Src1Alpha, + #[webidl(rename = "one-minus-src1-alpha")] + OneMinusSrc1Alpha, } impl From for wgpu_types::BlendFactor { - fn from(value: GPUBlendFactor) -> Self { - match value { - GPUBlendFactor::Zero => Self::Zero, - GPUBlendFactor::One => Self::One, - GPUBlendFactor::Src => Self::Src, - GPUBlendFactor::OneMinusSrc => Self::OneMinusSrc, - GPUBlendFactor::SrcAlpha => Self::SrcAlpha, - GPUBlendFactor::OneMinusSrcAlpha => Self::OneMinusSrcAlpha, - GPUBlendFactor::Dst => Self::Dst, - GPUBlendFactor::OneMinusDst => Self::OneMinusDst, - GPUBlendFactor::DstAlpha => Self::DstAlpha, - GPUBlendFactor::OneMinusDstAlpha => Self::OneMinusDstAlpha, - GPUBlendFactor::SrcAlphaSaturated => Self::SrcAlphaSaturated, - GPUBlendFactor::Constant => Self::Constant, - GPUBlendFactor::OneMinusConstant => Self::OneMinusConstant, - GPUBlendFactor::Src1 => Self::Src1, - GPUBlendFactor::OneMinusSrc1 => Self::OneMinusSrc1, - GPUBlendFactor::Src1Alpha => Self::Src1Alpha, - GPUBlendFactor::OneMinusSrc1Alpha => Self::OneMinusSrc1Alpha, - } + fn from(value: GPUBlendFactor) -> Self { + match value { + GPUBlendFactor::Zero => Self::Zero, + GPUBlendFactor::One => Self::One, + GPUBlendFactor::Src => Self::Src, + GPUBlendFactor::OneMinusSrc => Self::OneMinusSrc, + GPUBlendFactor::SrcAlpha => Self::SrcAlpha, + GPUBlendFactor::OneMinusSrcAlpha => Self::OneMinusSrcAlpha, + GPUBlendFactor::Dst => Self::Dst, + GPUBlendFactor::OneMinusDst => Self::OneMinusDst, + GPUBlendFactor::DstAlpha => Self::DstAlpha, + GPUBlendFactor::OneMinusDstAlpha => Self::OneMinusDstAlpha, + GPUBlendFactor::SrcAlphaSaturated => Self::SrcAlphaSaturated, + GPUBlendFactor::Constant => Self::Constant, + GPUBlendFactor::OneMinusConstant => Self::OneMinusConstant, + GPUBlendFactor::Src1 => Self::Src1, + GPUBlendFactor::OneMinusSrc1 => Self::OneMinusSrc1, + GPUBlendFactor::Src1Alpha => Self::Src1Alpha, + GPUBlendFactor::OneMinusSrc1Alpha => Self::OneMinusSrc1Alpha, } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUPrimitiveState { - #[webidl(default = GPUPrimitiveTopology::TriangleList)] - pub topology: GPUPrimitiveTopology, - pub strip_index_format: Option, - #[webidl(default = GPUFrontFace::Ccw)] - pub front_face: GPUFrontFace, - #[webidl(default = GPUCullMode::None)] - pub cull_mode: GPUCullMode, - #[webidl(default = false)] - pub unclipped_depth: bool, + #[webidl(default = GPUPrimitiveTopology::TriangleList)] + pub topology: GPUPrimitiveTopology, + pub strip_index_format: Option, + #[webidl(default = GPUFrontFace::Ccw)] + pub front_face: GPUFrontFace, + #[webidl(default = GPUCullMode::None)] + pub cull_mode: GPUCullMode, + #[webidl(default = false)] + pub unclipped_depth: bool, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUPrimitiveTopology { - PointList, - LineList, - LineStrip, - TriangleList, - TriangleStrip, + PointList, + LineList, + LineStrip, + TriangleList, + TriangleStrip, } impl From for wgpu_types::PrimitiveTopology { - fn from(value: GPUPrimitiveTopology) -> Self { - match value { - GPUPrimitiveTopology::PointList => Self::PointList, - GPUPrimitiveTopology::LineList => Self::LineList, - GPUPrimitiveTopology::LineStrip => Self::LineStrip, - GPUPrimitiveTopology::TriangleList => Self::TriangleList, - GPUPrimitiveTopology::TriangleStrip => Self::TriangleStrip, - } + fn from(value: GPUPrimitiveTopology) -> Self { + match value { + GPUPrimitiveTopology::PointList => Self::PointList, + GPUPrimitiveTopology::LineList => Self::LineList, + GPUPrimitiveTopology::LineStrip => Self::LineStrip, + GPUPrimitiveTopology::TriangleList => Self::TriangleList, + GPUPrimitiveTopology::TriangleStrip => Self::TriangleStrip, } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUIndexFormat { - #[webidl(rename = "uint16")] - Uint16, - #[webidl(rename = "uint32")] - Uint32, + #[webidl(rename = "uint16")] + Uint16, + #[webidl(rename = "uint32")] + Uint32, } impl From for wgpu_types::IndexFormat { - fn from(value: GPUIndexFormat) -> Self { - match value { - GPUIndexFormat::Uint16 => Self::Uint16, - GPUIndexFormat::Uint32 => Self::Uint32, - } + fn from(value: GPUIndexFormat) -> Self { + match value { + GPUIndexFormat::Uint16 => Self::Uint16, + GPUIndexFormat::Uint32 => Self::Uint32, } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUFrontFace { - Ccw, - Cw, + Ccw, + Cw, } impl From for wgpu_types::FrontFace { - fn from(value: GPUFrontFace) -> Self { - match value { - GPUFrontFace::Ccw => Self::Ccw, - GPUFrontFace::Cw => Self::Cw, - } + fn from(value: GPUFrontFace) -> Self { + match value { + GPUFrontFace::Ccw => Self::Ccw, + GPUFrontFace::Cw => Self::Cw, } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUCullMode { - None, - Front, - Back, + None, + Front, + Back, } impl From for Option { - fn from(value: GPUCullMode) -> Self { - match value { - GPUCullMode::None => None, - GPUCullMode::Front => Some(wgpu_types::Face::Front), - GPUCullMode::Back => Some(wgpu_types::Face::Back), - } + fn from(value: GPUCullMode) -> Self { + match value { + GPUCullMode::None => None, + GPUCullMode::Front => Some(wgpu_types::Face::Front), + GPUCullMode::Back => Some(wgpu_types::Face::Back), } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUVertexBufferLayout { - #[options(enforce_range = true)] - pub array_stride: u64, - #[webidl(default = GPUVertexStepMode::Vertex)] - pub step_mode: GPUVertexStepMode, - pub attributes: Vec, + #[options(enforce_range = true)] + pub array_stride: u64, + #[webidl(default = GPUVertexStepMode::Vertex)] + pub step_mode: GPUVertexStepMode, + pub attributes: Vec, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUVertexStepMode { - Vertex, - Instance, + Vertex, + Instance, } impl From for wgpu_types::VertexStepMode { - fn from(value: GPUVertexStepMode) -> Self { - match value { - GPUVertexStepMode::Vertex => Self::Vertex, - GPUVertexStepMode::Instance => Self::Instance, - } + fn from(value: GPUVertexStepMode) -> Self { + match value { + GPUVertexStepMode::Vertex => Self::Vertex, + GPUVertexStepMode::Instance => Self::Instance, } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUVertexAttribute { - pub format: GPUVertexFormat, - #[options(enforce_range = true)] - pub offset: u64, - #[options(enforce_range = true)] - pub shader_location: u32, + pub format: GPUVertexFormat, + #[options(enforce_range = true)] + pub offset: u64, + #[options(enforce_range = true)] + pub shader_location: u32, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUVertexFormat { - // #[webidl(rename = "uint8")] - // Uint8, - #[webidl(rename = "uint8x2")] - Uint8x2, - #[webidl(rename = "uint8x4")] - Uint8x4, - // #[webidl(rename = "sint8")] - // Sint8, - #[webidl(rename = "sint8x2")] - Sint8x2, - #[webidl(rename = "sint8x4")] - Sint8x4, - // #[webidl(rename = "unorm8")] - // Unorm8, - #[webidl(rename = "unorm8x2")] - Unorm8x2, - #[webidl(rename = "unorm8x4")] - Unorm8x4, - // #[webidl(rename = "snorm8")] - // Snorm8, - #[webidl(rename = "snorm8x2")] - Snorm8x2, - #[webidl(rename = "snorm8x4")] - Snorm8x4, - // #[webidl(rename = "uint16")] - // Uint16, - #[webidl(rename = "uint16x2")] - Uint16x2, - #[webidl(rename = "uint16x4")] - Uint16x4, - // #[webidl(rename = "sint16")] - // Sint16, - #[webidl(rename = "sint16x2")] - Sint16x2, - #[webidl(rename = "sint16x4")] - Sint16x4, - // #[webidl(rename = "unorm16")] - // Unorm16, - #[webidl(rename = "unorm16x2")] - Unorm16x2, - #[webidl(rename = "unorm16x4")] - Unorm16x4, - // #[webidl(rename = "snorm16")] - // Snorm16, - #[webidl(rename = "snorm16x2")] - Snorm16x2, - #[webidl(rename = "snorm16x4")] - Snorm16x4, - // #[webidl(rename = "float16")] - // Float16, - #[webidl(rename = "float16x2")] - Float16x2, - #[webidl(rename = "float16x4")] - Float16x4, - #[webidl(rename = "float32")] - Float32, - #[webidl(rename = "float32x2")] - Float32x2, - #[webidl(rename = "float32x3")] - Float32x3, - #[webidl(rename = "float32x4")] - Float32x4, - #[webidl(rename = "uint32")] - Uint32, - #[webidl(rename = "uint32x2")] - Uint32x2, - #[webidl(rename = "uint32x3")] - Uint32x3, - #[webidl(rename = "uint32x4")] - Uint32x4, - #[webidl(rename = "sint32")] - Sint32, - #[webidl(rename = "sint32x2")] - Sint32x2, - #[webidl(rename = "sint32x3")] - Sint32x3, - #[webidl(rename = "sint32x4")] - Sint32x4, - #[webidl(rename = "unorm10-10-10-2")] - Unorm1010102, - // #[webidl(rename = "unorm8x4-bgra")] - // Unorm8x4Bgra, + #[webidl(rename = "uint8")] + Uint8, + #[webidl(rename = "uint8x2")] + Uint8x2, + #[webidl(rename = "uint8x4")] + Uint8x4, + #[webidl(rename = "sint8")] + Sint8, + #[webidl(rename = "sint8x2")] + Sint8x2, + #[webidl(rename = "sint8x4")] + Sint8x4, + #[webidl(rename = "unorm8")] + Unorm8, + #[webidl(rename = "unorm8x2")] + Unorm8x2, + #[webidl(rename = "unorm8x4")] + Unorm8x4, + #[webidl(rename = "snorm8")] + Snorm8, + #[webidl(rename = "snorm8x2")] + Snorm8x2, + #[webidl(rename = "snorm8x4")] + Snorm8x4, + #[webidl(rename = "uint16")] + Uint16, + #[webidl(rename = "uint16x2")] + Uint16x2, + #[webidl(rename = "uint16x4")] + Uint16x4, + #[webidl(rename = "sint16")] + Sint16, + #[webidl(rename = "sint16x2")] + Sint16x2, + #[webidl(rename = "sint16x4")] + Sint16x4, + #[webidl(rename = "unorm16")] + Unorm16, + #[webidl(rename = "unorm16x2")] + Unorm16x2, + #[webidl(rename = "unorm16x4")] + Unorm16x4, + #[webidl(rename = "snorm16")] + Snorm16, + #[webidl(rename = "snorm16x2")] + Snorm16x2, + #[webidl(rename = "snorm16x4")] + Snorm16x4, + #[webidl(rename = "float16")] + Float16, + #[webidl(rename = "float16x2")] + Float16x2, + #[webidl(rename = "float16x4")] + Float16x4, + #[webidl(rename = "float32")] + Float32, + #[webidl(rename = "float32x2")] + Float32x2, + #[webidl(rename = "float32x3")] + Float32x3, + #[webidl(rename = "float32x4")] + Float32x4, + #[webidl(rename = "uint32")] + Uint32, + #[webidl(rename = "uint32x2")] + Uint32x2, + #[webidl(rename = "uint32x3")] + Uint32x3, + #[webidl(rename = "uint32x4")] + Uint32x4, + #[webidl(rename = "sint32")] + Sint32, + #[webidl(rename = "sint32x2")] + Sint32x2, + #[webidl(rename = "sint32x3")] + Sint32x3, + #[webidl(rename = "sint32x4")] + Sint32x4, + #[webidl(rename = "unorm10-10-10-2")] + Unorm1010102, + #[webidl(rename = "unorm8x4-bgra")] + Unorm8x4Bgra, } impl From for wgpu_types::VertexFormat { - fn from(value: GPUVertexFormat) -> Self { - match value { - //GPUVertexFormat::Uint8 => Self::Uint8, - GPUVertexFormat::Uint8x2 => Self::Uint8x2, - GPUVertexFormat::Uint8x4 => Self::Uint8x4, - //GPUVertexFormat::Sint8 => Self::Sint8, - GPUVertexFormat::Sint8x2 => Self::Sint8x2, - GPUVertexFormat::Sint8x4 => Self::Sint8x4, - //GPUVertexFormat::Unorm8 => Self::Unorm8, - GPUVertexFormat::Unorm8x2 => Self::Unorm8x2, - GPUVertexFormat::Unorm8x4 => Self::Unorm8x4, - //GPUVertexFormat::Snorm8 => Self::Snorm8, - GPUVertexFormat::Snorm8x2 => Self::Snorm8x2, - GPUVertexFormat::Snorm8x4 => Self::Snorm8x4, - //GPUVertexFormat::Uint16 => Self::Uint16, - GPUVertexFormat::Uint16x2 => Self::Uint16x2, - GPUVertexFormat::Uint16x4 => Self::Uint16x4, - //GPUVertexFormat::Sint16 => Self::Sint16, - GPUVertexFormat::Sint16x2 => Self::Sint16x2, - GPUVertexFormat::Sint16x4 => Self::Sint16x4, - //GPUVertexFormat::Unorm16 => Self::Unorm16, - GPUVertexFormat::Unorm16x2 => Self::Unorm16x2, - GPUVertexFormat::Unorm16x4 => Self::Unorm16x4, - //GPUVertexFormat::Snorm16 => Self::Snorm16, - GPUVertexFormat::Snorm16x2 => Self::Snorm16x2, - GPUVertexFormat::Snorm16x4 => Self::Snorm16x4, - //GPUVertexFormat::Float16 => Self::Float16, - GPUVertexFormat::Float16x2 => Self::Float16x2, - GPUVertexFormat::Float16x4 => Self::Float16x4, - GPUVertexFormat::Float32 => Self::Float32, - GPUVertexFormat::Float32x2 => Self::Float32x2, - GPUVertexFormat::Float32x3 => Self::Float32x3, - GPUVertexFormat::Float32x4 => Self::Float32x4, - GPUVertexFormat::Uint32 => Self::Uint32, - GPUVertexFormat::Uint32x2 => Self::Uint32x2, - GPUVertexFormat::Uint32x3 => Self::Uint32x3, - GPUVertexFormat::Uint32x4 => Self::Uint32x4, - GPUVertexFormat::Sint32 => Self::Sint32, - GPUVertexFormat::Sint32x2 => Self::Sint32x2, - GPUVertexFormat::Sint32x3 => Self::Sint32x3, - GPUVertexFormat::Sint32x4 => Self::Sint32x4, - GPUVertexFormat::Unorm1010102 => Self::Unorm10_10_10_2, - //GPUVertexFormat::Unorm8x4Bgra => Self::Unorm8x4Bgra, - } + fn from(value: GPUVertexFormat) -> Self { + match value { + GPUVertexFormat::Uint8 => Self::Uint8, + GPUVertexFormat::Uint8x2 => Self::Uint8x2, + GPUVertexFormat::Uint8x4 => Self::Uint8x4, + GPUVertexFormat::Sint8 => Self::Sint8, + GPUVertexFormat::Sint8x2 => Self::Sint8x2, + GPUVertexFormat::Sint8x4 => Self::Sint8x4, + GPUVertexFormat::Unorm8 => Self::Unorm8, + GPUVertexFormat::Unorm8x2 => Self::Unorm8x2, + GPUVertexFormat::Unorm8x4 => Self::Unorm8x4, + GPUVertexFormat::Snorm8 => Self::Snorm8, + GPUVertexFormat::Snorm8x2 => Self::Snorm8x2, + GPUVertexFormat::Snorm8x4 => Self::Snorm8x4, + GPUVertexFormat::Uint16 => Self::Uint16, + GPUVertexFormat::Uint16x2 => Self::Uint16x2, + GPUVertexFormat::Uint16x4 => Self::Uint16x4, + GPUVertexFormat::Sint16 => Self::Sint16, + GPUVertexFormat::Sint16x2 => Self::Sint16x2, + GPUVertexFormat::Sint16x4 => Self::Sint16x4, + GPUVertexFormat::Unorm16 => Self::Unorm16, + GPUVertexFormat::Unorm16x2 => Self::Unorm16x2, + GPUVertexFormat::Unorm16x4 => Self::Unorm16x4, + GPUVertexFormat::Snorm16 => Self::Snorm16, + GPUVertexFormat::Snorm16x2 => Self::Snorm16x2, + GPUVertexFormat::Snorm16x4 => Self::Snorm16x4, + GPUVertexFormat::Float16 => Self::Float16, + GPUVertexFormat::Float16x2 => Self::Float16x2, + GPUVertexFormat::Float16x4 => Self::Float16x4, + GPUVertexFormat::Float32 => Self::Float32, + GPUVertexFormat::Float32x2 => Self::Float32x2, + GPUVertexFormat::Float32x3 => Self::Float32x3, + GPUVertexFormat::Float32x4 => Self::Float32x4, + GPUVertexFormat::Uint32 => Self::Uint32, + GPUVertexFormat::Uint32x2 => Self::Uint32x2, + GPUVertexFormat::Uint32x3 => Self::Uint32x3, + GPUVertexFormat::Uint32x4 => Self::Uint32x4, + GPUVertexFormat::Sint32 => Self::Sint32, + GPUVertexFormat::Sint32x2 => Self::Sint32x2, + GPUVertexFormat::Sint32x3 => Self::Sint32x3, + GPUVertexFormat::Sint32x4 => Self::Sint32x4, + GPUVertexFormat::Unorm1010102 => Self::Unorm10_10_10_2, + GPUVertexFormat::Unorm8x4Bgra => Self::Unorm8x4Bgra, } + } } diff --git a/deno_webgpu/rustfmt.toml b/deno_webgpu/rustfmt.toml new file mode 100644 index 00000000000..9bb8d9d4584 --- /dev/null +++ b/deno_webgpu/rustfmt.toml @@ -0,0 +1,3 @@ +max_width = 80 +tab_spaces = 2 +edition = "2021" diff --git a/deno_webgpu/sampler.rs b/deno_webgpu/sampler.rs index 46c08bc7455..c6dd44339f3 100644 --- a/deno_webgpu/sampler.rs +++ b/deno_webgpu/sampler.rs @@ -5,130 +5,141 @@ use deno_core::webidl::WebIdlInterfaceConverter; use deno_core::GarbageCollected; use deno_core::WebIDL; +use crate::error::GPUGenericError; use crate::Instance; pub struct GPUSampler { - pub instance: Instance, - pub id: wgpu_core::id::SamplerId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::SamplerId, + pub label: String, } impl Drop for GPUSampler { - fn drop(&mut self) { - self.instance.sampler_drop(self.id); - } + fn drop(&mut self) { + self.instance.sampler_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUSampler { - const NAME: &'static str = "GPUSampler"; + const NAME: &'static str = "GPUSampler"; } -impl GarbageCollected for GPUSampler {} +impl GarbageCollected for GPUSampler { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUSampler" + } +} #[op2] impl GPUSampler { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(super) struct GPUSamplerDescriptor { - #[webidl(default = String::new())] - pub label: String, - - #[webidl(default = GPUAddressMode::ClampToEdge)] - pub address_mode_u: GPUAddressMode, - #[webidl(default = GPUAddressMode::ClampToEdge)] - pub address_mode_v: GPUAddressMode, - #[webidl(default = GPUAddressMode::ClampToEdge)] - pub address_mode_w: GPUAddressMode, - #[webidl(default = GPUFilterMode::Nearest)] - pub mag_filter: GPUFilterMode, - #[webidl(default = GPUFilterMode::Nearest)] - pub min_filter: GPUFilterMode, - #[webidl(default = GPUFilterMode::Nearest)] - pub mipmap_filter: GPUFilterMode, - - #[webidl(default = 0.0)] - pub lod_min_clamp: f32, - #[webidl(default = 32.0)] - pub lod_max_clamp: f32, - - pub compare: Option, - - #[webidl(default = 1)] - #[options(clamp = true)] - pub max_anisotropy: u16, + #[webidl(default = String::new())] + pub label: String, + + #[webidl(default = GPUAddressMode::ClampToEdge)] + pub address_mode_u: GPUAddressMode, + #[webidl(default = GPUAddressMode::ClampToEdge)] + pub address_mode_v: GPUAddressMode, + #[webidl(default = GPUAddressMode::ClampToEdge)] + pub address_mode_w: GPUAddressMode, + #[webidl(default = GPUFilterMode::Nearest)] + pub mag_filter: GPUFilterMode, + #[webidl(default = GPUFilterMode::Nearest)] + pub min_filter: GPUFilterMode, + #[webidl(default = GPUFilterMode::Nearest)] + pub mipmap_filter: GPUFilterMode, + + #[webidl(default = 0.0)] + pub lod_min_clamp: f32, + #[webidl(default = 32.0)] + pub lod_max_clamp: f32, + + pub compare: Option, + + #[webidl(default = 1)] + #[options(clamp = true)] + pub max_anisotropy: u16, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUAddressMode { - ClampToEdge, - Repeat, - MirrorRepeat, + ClampToEdge, + Repeat, + MirrorRepeat, } impl From for wgpu_types::AddressMode { - fn from(value: GPUAddressMode) -> Self { - match value { - GPUAddressMode::ClampToEdge => Self::ClampToEdge, - GPUAddressMode::Repeat => Self::Repeat, - GPUAddressMode::MirrorRepeat => Self::MirrorRepeat, - } + fn from(value: GPUAddressMode) -> Self { + match value { + GPUAddressMode::ClampToEdge => Self::ClampToEdge, + GPUAddressMode::Repeat => Self::Repeat, + GPUAddressMode::MirrorRepeat => Self::MirrorRepeat, } + } } // Same as GPUMipmapFilterMode #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUFilterMode { - Nearest, - Linear, + Nearest, + Linear, } impl From for wgpu_types::FilterMode { - fn from(value: GPUFilterMode) -> Self { - match value { - GPUFilterMode::Nearest => Self::Nearest, - GPUFilterMode::Linear => Self::Linear, - } + fn from(value: GPUFilterMode) -> Self { + match value { + GPUFilterMode::Nearest => Self::Nearest, + GPUFilterMode::Linear => Self::Linear, } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUCompareFunction { - Never, - Less, - Equal, - LessEqual, - Greater, - NotEqual, - GreaterEqual, - Always, + Never, + Less, + Equal, + LessEqual, + Greater, + NotEqual, + GreaterEqual, + Always, } impl From for wgpu_types::CompareFunction { - fn from(value: GPUCompareFunction) -> Self { - match value { - GPUCompareFunction::Never => Self::Never, - GPUCompareFunction::Less => Self::Less, - GPUCompareFunction::Equal => Self::Equal, - GPUCompareFunction::LessEqual => Self::LessEqual, - GPUCompareFunction::Greater => Self::Greater, - GPUCompareFunction::NotEqual => Self::NotEqual, - GPUCompareFunction::GreaterEqual => Self::GreaterEqual, - GPUCompareFunction::Always => Self::Always, - } + fn from(value: GPUCompareFunction) -> Self { + match value { + GPUCompareFunction::Never => Self::Never, + GPUCompareFunction::Less => Self::Less, + GPUCompareFunction::Equal => Self::Equal, + GPUCompareFunction::LessEqual => Self::LessEqual, + GPUCompareFunction::Greater => Self::Greater, + GPUCompareFunction::NotEqual => Self::NotEqual, + GPUCompareFunction::GreaterEqual => Self::GreaterEqual, + GPUCompareFunction::Always => Self::Always, } + } } diff --git a/deno_webgpu/shader.rs b/deno_webgpu/shader.rs index 43563f89573..49a81a83c94 100644 --- a/deno_webgpu/shader.rs +++ b/deno_webgpu/shader.rs @@ -8,196 +8,219 @@ use deno_core::GarbageCollected; use deno_core::WebIDL; use wgpu_core::pipeline; +use crate::error::GPUGenericError; use crate::Instance; pub struct GPUShaderModule { - pub instance: Instance, - pub id: wgpu_core::id::ShaderModuleId, - pub label: String, - pub compilation_info: v8::Global, + pub instance: Instance, + pub id: wgpu_core::id::ShaderModuleId, + pub label: String, + pub compilation_info: v8::Global, } impl Drop for GPUShaderModule { - fn drop(&mut self) { - self.instance.shader_module_drop(self.id); - } + fn drop(&mut self) { + self.instance.shader_module_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUShaderModule { - const NAME: &'static str = "GPUShaderModule"; + const NAME: &'static str = "GPUShaderModule"; } -impl GarbageCollected for GPUShaderModule {} +impl GarbageCollected for GPUShaderModule { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUShaderModule" + } +} #[op2] impl GPUShaderModule { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - fn get_compilation_info<'a>( - &self, - scope: &mut v8::HandleScope<'a>, - ) -> v8::Local<'a, v8::Promise> { - let resolver = v8::PromiseResolver::new(scope).unwrap(); - let info = v8::Local::new(scope, self.compilation_info.clone()); - resolver.resolve(scope, info.into()).unwrap(); - resolver.get_promise(scope) - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + fn get_compilation_info<'a>( + &self, + scope: &mut v8::HandleScope<'a>, + ) -> v8::Local<'a, v8::Promise> { + let resolver = v8::PromiseResolver::new(scope).unwrap(); + let info = v8::Local::new(scope, self.compilation_info.clone()); + resolver.resolve(scope, info.into()).unwrap(); + resolver.get_promise(scope) + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUShaderModuleDescriptor { - #[webidl(default = String::new())] - pub label: String, + #[webidl(default = String::new())] + pub label: String, - pub code: String, + pub code: String, } pub struct GPUCompilationMessage { - message: String, - r#type: GPUCompilationMessageType, - line_num: u64, - line_pos: u64, - offset: u64, - length: u64, + message: String, + r#type: GPUCompilationMessageType, + line_num: u64, + line_pos: u64, + offset: u64, + length: u64, } -impl GarbageCollected for GPUCompilationMessage {} +impl GarbageCollected for GPUCompilationMessage { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUCompilationMessage" + } +} #[op2] impl GPUCompilationMessage { - #[getter] - #[string] - fn message(&self) -> String { - self.message.clone() - } + #[getter] + #[string] + fn message(&self) -> String { + self.message.clone() + } + + #[getter] + #[string] + #[rename("type")] + fn r#type(&self) -> &'static str { + self.r#type.as_str() + } + + #[getter] + #[number] + fn line_num(&self) -> u64 { + self.line_num + } + + #[getter] + #[number] + fn line_pos(&self) -> u64 { + self.line_pos + } + + #[getter] + #[number] + fn offset(&self) -> u64 { + self.offset + } + + #[getter] + #[number] + fn length(&self) -> u64 { + self.length + } +} - // Naming this `type` or `r#type` does not work. - // https://github.com/gfx-rs/wgpu/issues/7778 - #[getter] - #[string] - fn ty(&self) -> &'static str { - self.r#type.as_str() - } +impl GPUCompilationMessage { + fn new(error: &pipeline::CreateShaderModuleError, source: &str) -> Self { + let message = error.to_string(); - #[getter] - #[number] - fn line_num(&self) -> u64 { - self.line_num - } + let loc = match error { + pipeline::CreateShaderModuleError::Parsing(e) => e.inner.location(source), + pipeline::CreateShaderModuleError::Validation(e) => { + e.inner.location(source) + } + _ => None, + }; - #[getter] - #[number] - fn line_pos(&self) -> u64 { - self.line_pos - } + match loc { + Some(loc) => { + let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum(); - #[getter] - #[number] - fn offset(&self) -> u64 { - self.offset - } + let start = loc.offset as usize; - #[getter] - #[number] - fn length(&self) -> u64 { - self.length - } -} + // Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it. + let line_start = + source[0..start].rfind('\n').map(|pos| pos + 1).unwrap_or(0); + let line_pos = len_utf16(&source[line_start..start]) + 1; -impl GPUCompilationMessage { - fn new(error: &pipeline::CreateShaderModuleError, source: &str) -> Self { - let message = error.to_string(); - - let loc = match error { - pipeline::CreateShaderModuleError::Parsing(e) => e.inner.location(source), - pipeline::CreateShaderModuleError::Validation(e) => e.inner.location(source), - _ => None, - }; - - match loc { - Some(loc) => { - let len_utf16 = |s: &str| s.chars().map(|c| c.len_utf16() as u64).sum(); - - let start = loc.offset as usize; - - // Naga reports a `line_pos` using UTF-8 bytes, so we cannot use it. - let line_start = source[0..start].rfind('\n').map(|pos| pos + 1).unwrap_or(0); - let line_pos = len_utf16(&source[line_start..start]) + 1; - - Self { - message, - r#type: GPUCompilationMessageType::Error, - line_num: loc.line_number.into(), - line_pos, - offset: len_utf16(&source[0..start]), - length: len_utf16(&source[start..start + loc.length as usize]), - } - } - _ => Self { - message, - r#type: GPUCompilationMessageType::Error, - line_num: 0, - line_pos: 0, - offset: 0, - length: 0, - }, + Self { + message, + r#type: GPUCompilationMessageType::Error, + line_num: loc.line_number.into(), + line_pos, + offset: len_utf16(&source[0..start]), + length: len_utf16(&source[start..start + loc.length as usize]), } + } + _ => Self { + message, + r#type: GPUCompilationMessageType::Error, + line_num: 0, + line_pos: 0, + offset: 0, + length: 0, + }, } + } } pub struct GPUCompilationInfo { - messages: v8::Global, + messages: v8::Global, } -impl GarbageCollected for GPUCompilationInfo {} +impl GarbageCollected for GPUCompilationInfo { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUCompilationInfo" + } +} #[op2] impl GPUCompilationInfo { - #[getter] - #[global] - fn messages(&self) -> v8::Global { - self.messages.clone() - } + #[getter] + #[global] + fn messages(&self) -> v8::Global { + self.messages.clone() + } } impl GPUCompilationInfo { - pub fn new<'args, 'scope>( - scope: &mut v8::HandleScope<'scope>, - messages: impl ExactSizeIterator, - source: &'args str, - ) -> Self { - let array = v8::Array::new(scope, messages.len().try_into().unwrap()); - for (i, message) in messages.enumerate() { - let message_object = - make_cppgc_object(scope, GPUCompilationMessage::new(message, source)); - array.set_index(scope, i.try_into().unwrap(), message_object.into()); - } + pub fn new<'args, 'scope>( + scope: &mut v8::HandleScope<'scope>, + messages: impl ExactSizeIterator< + Item = &'args pipeline::CreateShaderModuleError, + >, + source: &'args str, + ) -> Self { + let array = v8::Array::new(scope, messages.len().try_into().unwrap()); + for (i, message) in messages.enumerate() { + let message_object = + make_cppgc_object(scope, GPUCompilationMessage::new(message, source)); + array.set_index(scope, i.try_into().unwrap(), message_object.into()); + } - let object: v8::Local = array.into(); - object - .set_integrity_level(scope, v8::IntegrityLevel::Frozen) - .unwrap(); + let object: v8::Local = array.into(); + object + .set_integrity_level(scope, v8::IntegrityLevel::Frozen) + .unwrap(); - Self { - messages: v8::Global::new(scope, object), - } + Self { + messages: v8::Global::new(scope, object), } + } } #[derive(WebIDL, Clone)] #[webidl(enum)] pub(crate) enum GPUCompilationMessageType { - Error, - Warning, - Info, + Error, + Warning, + Info, } diff --git a/deno_webgpu/surface.rs b/deno_webgpu/surface.rs index 8c8445a9a4f..60df27ce0e1 100644 --- a/deno_webgpu/surface.rs +++ b/deno_webgpu/surface.rs @@ -2,228 +2,273 @@ use std::cell::RefCell; -use deno_core::op2; -use deno_core::v8; use deno_core::GarbageCollected; use deno_core::WebIDL; use deno_core::_ops::make_cppgc_object; use deno_core::cppgc::Ptr; +use deno_core::op2; +use deno_core::v8; use deno_error::JsErrorBox; use wgpu_types::SurfaceStatus; use crate::device::GPUDevice; +use crate::error::GPUGenericError; use crate::texture::GPUTexture; use crate::texture::GPUTextureFormat; #[derive(Debug, thiserror::Error, deno_error::JsError)] pub enum SurfaceError { - #[class("DOMExceptionInvalidStateError")] - #[error("Context is not configured")] - UnconfiguredContext, - #[class(generic)] - #[error("Invalid Surface Status")] - InvalidStatus, - #[class(generic)] - #[error(transparent)] - Surface(#[from] wgpu_core::present::SurfaceError), + #[class("DOMExceptionInvalidStateError")] + #[error("Context is not configured")] + UnconfiguredContext, + #[class(generic)] + #[error("Invalid Surface Status")] + InvalidStatus, + #[class(generic)] + #[error(transparent)] + Surface(#[from] wgpu_core::present::SurfaceError), } pub struct Configuration { - pub device: Ptr, - pub usage: u32, - pub format: GPUTextureFormat, + pub device: Ptr, + pub usage: u32, + pub format: GPUTextureFormat, + pub surface_config: + wgpu_types::SurfaceConfiguration>, } pub struct GPUCanvasContext { - pub surface_id: wgpu_core::id::SurfaceId, - pub width: RefCell, - pub height: RefCell, + pub surface_id: wgpu_core::id::SurfaceId, + pub width: RefCell, + pub height: RefCell, - pub config: RefCell>, - pub texture: RefCell>>, + pub config: RefCell>, + pub texture: RefCell>>, - pub canvas: v8::Global, + pub canvas: v8::Global, } -impl GarbageCollected for GPUCanvasContext {} +impl GarbageCollected for GPUCanvasContext { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUCanvasContext" + } +} #[op2] impl GPUCanvasContext { - #[getter] - #[global] - fn canvas(&self) -> v8::Global { - self.canvas.clone() + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[global] + fn canvas(&self) -> v8::Global { + self.canvas.clone() + } + + #[undefined] + fn configure( + &self, + #[webidl] configuration: GPUCanvasConfiguration, + ) -> Result<(), JsErrorBox> { + let usage = wgpu_types::TextureUsages::from_bits(configuration.usage) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?; + let format = configuration.format.clone().into(); + let conf = wgpu_types::SurfaceConfiguration { + usage, + format, + width: *self.width.borrow(), + height: *self.height.borrow(), + present_mode: configuration + .present_mode + .map(Into::into) + .unwrap_or_default(), + alpha_mode: configuration.alpha_mode.into(), + view_formats: configuration + .view_formats + .into_iter() + .map(Into::into) + .collect(), + desired_maximum_frame_latency: 2, + }; + + let device = configuration.device; + + let err = + device + .instance + .surface_configure(self.surface_id, device.id, &conf); + + device.error_handler.push_error(err); + + self.config.borrow_mut().replace(Configuration { + device, + usage: configuration.usage, + format: configuration.format, + surface_config: conf, + }); + + Ok(()) + } + + #[fast] + #[undefined] + fn unconfigure(&self) { + *self.config.borrow_mut() = None; + } + + #[global] + fn get_current_texture( + &self, + scope: &mut v8::HandleScope, + ) -> Result, SurfaceError> { + let config = self.config.borrow(); + let Some(config) = config.as_ref() else { + return Err(SurfaceError::UnconfiguredContext); + }; + + { + if let Some(obj) = self.texture.borrow().as_ref() { + return Ok(obj.clone()); + } } - fn configure(&self, #[webidl] configuration: GPUCanvasConfiguration) -> Result<(), JsErrorBox> { - let usage = wgpu_types::TextureUsages::from_bits(configuration.usage) - .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?; - let format = configuration.format.clone().into(); - let conf = wgpu_types::SurfaceConfiguration { - usage, - format, + let output = config + .device + .instance + .surface_get_current_texture(self.surface_id, None)?; + + match output.status { + SurfaceStatus::Good | SurfaceStatus::Suboptimal => { + let id = output.texture.unwrap(); + + let texture = GPUTexture { + instance: config.device.instance.clone(), + error_handler: config.device.error_handler.clone(), + id, + default_view_id: Default::default(), + label: "".to_string(), + size: wgpu_types::Extent3d { width: *self.width.borrow(), height: *self.height.borrow(), - present_mode: configuration - .present_mode - .map(Into::into) - .unwrap_or_default(), - alpha_mode: configuration.alpha_mode.into(), - view_formats: configuration - .view_formats - .into_iter() - .map(Into::into) - .collect(), - desired_maximum_frame_latency: 2, + depth_or_array_layers: 1, + }, + mip_level_count: 0, + sample_count: 0, + dimension: crate::texture::GPUTextureDimension::D2, + format: config.format.clone(), + usage: config.usage, }; + let obj = make_cppgc_object(scope, texture); + let obj = v8::Global::new(scope, obj); + *self.texture.borrow_mut() = Some(obj.clone()); - let device = configuration.device; + Ok(obj) + } + _ => Err(SurfaceError::InvalidStatus), + } + } +} - let err = device - .instance - .surface_configure(self.surface_id, device.id, &conf); +impl GPUCanvasContext { + pub fn present(&self) -> Result<(), SurfaceError> { + let config = self.config.borrow(); + let Some(config) = config.as_ref() else { + return Err(SurfaceError::UnconfiguredContext); + }; - device.error_handler.push_error(err); + config.device.instance.surface_present(self.surface_id)?; - self.config.borrow_mut().replace(Configuration { - device, - usage: configuration.usage, - format: configuration.format, - }); + // next `get_current_texture` call would get a new texture + *self.texture.borrow_mut() = None; - Ok(()) - } + Ok(()) + } - #[fast] - fn unconfigure(&self) { - *self.config.borrow_mut() = None; - } - - #[global] - fn get_current_texture( - &self, - scope: &mut v8::HandleScope, - ) -> Result, SurfaceError> { - let config = self.config.borrow(); - let Some(config) = config.as_ref() else { - return Err(SurfaceError::UnconfiguredContext); - }; + pub fn resize_configure(&self, width: u32, height: u32) { + self.width.replace(width); + self.height.replace(height); - { - if let Some(obj) = self.texture.borrow().as_ref() { - return Ok(obj.clone()); - } - } - - let output = config - .device - .instance - .surface_get_current_texture(self.surface_id, None)?; - - match output.status { - SurfaceStatus::Good | SurfaceStatus::Suboptimal => { - let id = output.texture.unwrap(); - - let texture = GPUTexture { - instance: config.device.instance.clone(), - error_handler: config.device.error_handler.clone(), - id, - label: "".to_string(), - size: wgpu_types::Extent3d { - width: *self.width.borrow(), - height: *self.height.borrow(), - depth_or_array_layers: 1, - }, - mip_level_count: 0, - sample_count: 0, - dimension: crate::texture::GPUTextureDimension::D2, - format: config.format.clone(), - usage: config.usage, - }; - let obj = make_cppgc_object(scope, texture); - let obj = v8::Global::new(scope, obj); - *self.texture.borrow_mut() = Some(obj.clone()); - - Ok(obj) - } - _ => Err(SurfaceError::InvalidStatus), - } - } -} + let mut config = self.config.borrow_mut(); + let Some(config) = &mut *config else { + return; + }; -impl GPUCanvasContext { - pub fn present(&self) -> Result<(), SurfaceError> { - let config = self.config.borrow(); - let Some(config) = config.as_ref() else { - return Err(SurfaceError::UnconfiguredContext); - }; + config.surface_config.width = width; + config.surface_config.height = height; - config.device.instance.surface_present(self.surface_id)?; + let err = config.device.instance.surface_configure( + self.surface_id, + config.device.id, + &config.surface_config, + ); - Ok(()) - } + config.device.error_handler.push_error(err); + } } #[derive(WebIDL)] #[webidl(dictionary)] struct GPUCanvasConfiguration { - device: Ptr, - format: GPUTextureFormat, - #[webidl(default = wgpu_types::TextureUsages::RENDER_ATTACHMENT.bits())] - #[options(enforce_range = true)] - usage: u32, - #[webidl(default = GPUCanvasAlphaMode::Opaque)] - alpha_mode: GPUCanvasAlphaMode, - - // Extended from spec - present_mode: Option, - #[webidl(default = vec![])] - view_formats: Vec, + device: Ptr, + format: GPUTextureFormat, + #[webidl(default = wgpu_types::TextureUsages::RENDER_ATTACHMENT.bits())] + #[options(enforce_range = true)] + usage: u32, + #[webidl(default = GPUCanvasAlphaMode::Opaque)] + alpha_mode: GPUCanvasAlphaMode, + + // Extended from spec + present_mode: Option, + #[webidl(default = vec![])] + view_formats: Vec, } #[derive(WebIDL)] #[webidl(enum)] enum GPUCanvasAlphaMode { - Opaque, - Premultiplied, + Opaque, + Premultiplied, } impl From for wgpu_types::CompositeAlphaMode { - fn from(value: GPUCanvasAlphaMode) -> Self { - match value { - GPUCanvasAlphaMode::Opaque => Self::Opaque, - GPUCanvasAlphaMode::Premultiplied => Self::PreMultiplied, - } + fn from(value: GPUCanvasAlphaMode) -> Self { + match value { + GPUCanvasAlphaMode::Opaque => Self::Opaque, + GPUCanvasAlphaMode::Premultiplied => Self::PreMultiplied, } + } } // Extended from spec #[derive(WebIDL)] #[webidl(enum)] enum GPUPresentMode { - #[webidl(rename = "autoVsync")] - AutoVsync, - #[webidl(rename = "autoNoVsync")] - AutoNoVsync, - #[webidl(rename = "fifo")] - Fifo, - #[webidl(rename = "fifoRelaxed")] - FifoRelaxed, - #[webidl(rename = "immediate")] - Immediate, - #[webidl(rename = "mailbox")] - Mailbox, + #[webidl(rename = "autoVsync")] + AutoVsync, + #[webidl(rename = "autoNoVsync")] + AutoNoVsync, + #[webidl(rename = "fifo")] + Fifo, + #[webidl(rename = "fifoRelaxed")] + FifoRelaxed, + #[webidl(rename = "immediate")] + Immediate, + #[webidl(rename = "mailbox")] + Mailbox, } impl From for wgpu_types::PresentMode { - fn from(value: GPUPresentMode) -> Self { - match value { - GPUPresentMode::AutoVsync => Self::AutoVsync, - GPUPresentMode::AutoNoVsync => Self::AutoNoVsync, - GPUPresentMode::Fifo => Self::Fifo, - GPUPresentMode::FifoRelaxed => Self::FifoRelaxed, - GPUPresentMode::Immediate => Self::Immediate, - GPUPresentMode::Mailbox => Self::Mailbox, - } + fn from(value: GPUPresentMode) -> Self { + match value { + GPUPresentMode::AutoVsync => Self::AutoVsync, + GPUPresentMode::AutoNoVsync => Self::AutoNoVsync, + GPUPresentMode::Fifo => Self::Fifo, + GPUPresentMode::FifoRelaxed => Self::FifoRelaxed, + GPUPresentMode::Immediate => Self::Immediate, + GPUPresentMode::Mailbox => Self::Mailbox, } + } } diff --git a/deno_webgpu/texture.rs b/deno_webgpu/texture.rs index 7a7af62c59f..8d92d7f308a 100644 --- a/deno_webgpu/texture.rs +++ b/deno_webgpu/texture.rs @@ -1,5 +1,7 @@ // Copyright 2018-2025 the Deno authors. MIT license. +use std::sync::OnceLock; + use deno_core::op2; use deno_core::webidl::WebIdlInterfaceConverter; use deno_core::GarbageCollected; @@ -13,661 +15,716 @@ use wgpu_types::TextureDimension; use wgpu_types::TextureFormat; use wgpu_types::TextureViewDimension; +use crate::error::GPUGenericError; use crate::Instance; #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUTextureDescriptor { - #[webidl(default = String::new())] - pub label: String, - - pub size: super::webidl::GPUExtent3D, - #[webidl(default = 1)] - #[options(enforce_range = true)] - pub mip_level_count: u32, - #[webidl(default = 1)] - #[options(enforce_range = true)] - pub sample_count: u32, - #[webidl(default = GPUTextureDimension::D2)] - pub dimension: GPUTextureDimension, - pub format: GPUTextureFormat, - #[options(enforce_range = true)] - pub usage: u32, - #[webidl(default = vec![])] - pub view_formats: Vec, + #[webidl(default = String::new())] + pub label: String, + + pub size: super::webidl::GPUExtent3D, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub mip_level_count: u32, + #[webidl(default = 1)] + #[options(enforce_range = true)] + pub sample_count: u32, + #[webidl(default = GPUTextureDimension::D2)] + pub dimension: GPUTextureDimension, + pub format: GPUTextureFormat, + #[options(enforce_range = true)] + pub usage: u32, + #[webidl(default = vec![])] + pub view_formats: Vec, } pub struct GPUTexture { - pub instance: Instance, - pub error_handler: super::error::ErrorHandler, + pub instance: Instance, + pub error_handler: super::error::ErrorHandler, + + pub id: wgpu_core::id::TextureId, + pub default_view_id: OnceLock, - pub id: wgpu_core::id::TextureId, + pub label: String, - pub label: String, + pub size: Extent3d, + pub mip_level_count: u32, + pub sample_count: u32, + pub dimension: GPUTextureDimension, + pub format: GPUTextureFormat, + pub usage: u32, +} - pub size: Extent3d, - pub mip_level_count: u32, - pub sample_count: u32, - pub dimension: GPUTextureDimension, - pub format: GPUTextureFormat, - pub usage: u32, +impl GPUTexture { + pub(crate) fn default_view_id(&self) -> wgpu_core::id::TextureViewId { + *self.default_view_id.get_or_init(|| { + let (id, err) = + self + .instance + .texture_create_view(self.id, &Default::default(), None); + if let Some(err) = err { + use wgpu_types::error::WebGpuError; + assert_ne!( + err.webgpu_error_type(), + wgpu_types::error::ErrorType::Validation, + concat!( + "getting default view for a texture ", + "caused a validation error (!?)" + ) + ); + self.error_handler.push_error(Some(err)); + } + id + }) + } } impl Drop for GPUTexture { - fn drop(&mut self) { - self.instance.texture_drop(self.id); + fn drop(&mut self) { + if let Some(id) = self.default_view_id.take() { + self.instance.texture_view_drop(id).unwrap(); } + self.instance.texture_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUTexture { - const NAME: &'static str = "GPUTexture"; + const NAME: &'static str = "GPUTexture"; } -impl GarbageCollected for GPUTexture {} +impl GarbageCollected for GPUTexture { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUTexture" + } +} #[op2] impl GPUTexture { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } - - #[getter] - fn width(&self) -> u32 { - self.size.width - } - #[getter] - fn height(&self) -> u32 { - self.size.height - } - #[getter] - fn depth_or_array_layers(&self) -> u32 { - self.size.depth_or_array_layers - } - #[getter] - fn mip_level_count(&self) -> u32 { - self.mip_level_count - } - #[getter] - fn sample_count(&self) -> u32 { - self.sample_count - } - #[getter] - #[string] - fn dimension(&self) -> &'static str { - self.dimension.as_str() - } - #[getter] - #[string] - fn format(&self) -> &'static str { - self.format.as_str() - } - #[getter] - fn usage(&self) -> u32 { - self.usage - } - #[fast] - fn destroy(&self) { - self.instance.texture_destroy(self.id); - } - - #[cppgc] - fn create_view( - &self, - #[webidl] descriptor: GPUTextureViewDescriptor, - ) -> Result { - let wgpu_descriptor = wgpu_core::resource::TextureViewDescriptor { - label: crate::transform_label(descriptor.label.clone()), - format: descriptor.format.map(Into::into), - dimension: descriptor.dimension.map(Into::into), - usage: Some( - wgpu_types::TextureUsages::from_bits(descriptor.usage) - .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, - ), - range: wgpu_types::ImageSubresourceRange { - aspect: descriptor.aspect.into(), - base_mip_level: descriptor.base_mip_level, - mip_level_count: descriptor.mip_level_count, - base_array_layer: descriptor.base_array_layer, - array_layer_count: descriptor.array_layer_count, - }, - }; - - let (id, err) = self - .instance - .texture_create_view(self.id, &wgpu_descriptor, None); - - self.error_handler.push_error(err); - - Ok(GPUTextureView { - instance: self.instance.clone(), - id, - label: descriptor.label, - }) - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } + + #[getter] + fn width(&self) -> u32 { + self.size.width + } + #[getter] + fn height(&self) -> u32 { + self.size.height + } + #[getter] + fn depth_or_array_layers(&self) -> u32 { + self.size.depth_or_array_layers + } + #[getter] + fn mip_level_count(&self) -> u32 { + self.mip_level_count + } + #[getter] + fn sample_count(&self) -> u32 { + self.sample_count + } + #[getter] + #[string] + fn dimension(&self) -> &'static str { + self.dimension.as_str() + } + #[getter] + #[string] + fn format(&self) -> &'static str { + self.format.as_str() + } + #[getter] + fn usage(&self) -> u32 { + self.usage + } + #[fast] + #[undefined] + fn destroy(&self) { + self.instance.texture_destroy(self.id); + } + + #[cppgc] + fn create_view( + &self, + #[webidl] descriptor: GPUTextureViewDescriptor, + ) -> Result { + let wgpu_descriptor = wgpu_core::resource::TextureViewDescriptor { + label: crate::transform_label(descriptor.label.clone()), + format: descriptor.format.map(Into::into), + dimension: descriptor.dimension.map(Into::into), + usage: Some( + wgpu_types::TextureUsages::from_bits(descriptor.usage) + .ok_or_else(|| JsErrorBox::type_error("usage is not valid"))?, + ), + range: wgpu_types::ImageSubresourceRange { + aspect: descriptor.aspect.into(), + base_mip_level: descriptor.base_mip_level, + mip_level_count: descriptor.mip_level_count, + base_array_layer: descriptor.base_array_layer, + array_layer_count: descriptor.array_layer_count, + }, + }; + + let (id, err) = + self + .instance + .texture_create_view(self.id, &wgpu_descriptor, None); + + self.error_handler.push_error(err); + + Ok(GPUTextureView { + instance: self.instance.clone(), + id, + label: descriptor.label, + }) + } } #[derive(WebIDL)] #[webidl(dictionary)] struct GPUTextureViewDescriptor { - #[webidl(default = String::new())] - label: String, - - format: Option, - dimension: Option, - #[webidl(default = 0)] - #[options(enforce_range = true)] - usage: u32, - #[webidl(default = GPUTextureAspect::All)] - aspect: GPUTextureAspect, - #[webidl(default = 0)] - #[options(enforce_range = true)] - base_mip_level: u32, - #[options(enforce_range = true)] - mip_level_count: Option, - #[webidl(default = 0)] - #[options(enforce_range = true)] - base_array_layer: u32, - #[options(enforce_range = true)] - array_layer_count: Option, + #[webidl(default = String::new())] + label: String, + + format: Option, + dimension: Option, + #[webidl(default = 0)] + #[options(enforce_range = true)] + usage: u32, + #[webidl(default = GPUTextureAspect::All)] + aspect: GPUTextureAspect, + #[webidl(default = 0)] + #[options(enforce_range = true)] + base_mip_level: u32, + #[options(enforce_range = true)] + mip_level_count: Option, + #[webidl(default = 0)] + #[options(enforce_range = true)] + base_array_layer: u32, + #[options(enforce_range = true)] + array_layer_count: Option, } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUTextureViewDimension { - #[webidl(rename = "1d")] - D1, - #[webidl(rename = "2d")] - D2, - #[webidl(rename = "2d-array")] - D2Array, - #[webidl(rename = "cube")] - Cube, - #[webidl(rename = "cube-array")] - CubeArray, - #[webidl(rename = "3d")] - D3, + #[webidl(rename = "1d")] + D1, + #[webidl(rename = "2d")] + D2, + #[webidl(rename = "2d-array")] + D2Array, + #[webidl(rename = "cube")] + Cube, + #[webidl(rename = "cube-array")] + CubeArray, + #[webidl(rename = "3d")] + D3, } impl From for TextureViewDimension { - fn from(value: GPUTextureViewDimension) -> Self { - match value { - GPUTextureViewDimension::D1 => Self::D1, - GPUTextureViewDimension::D2 => Self::D2, - GPUTextureViewDimension::D3 => Self::D3, - GPUTextureViewDimension::D2Array => Self::D2Array, - GPUTextureViewDimension::Cube => Self::Cube, - GPUTextureViewDimension::CubeArray => Self::CubeArray, - } + fn from(value: GPUTextureViewDimension) -> Self { + match value { + GPUTextureViewDimension::D1 => Self::D1, + GPUTextureViewDimension::D2 => Self::D2, + GPUTextureViewDimension::D3 => Self::D3, + GPUTextureViewDimension::D2Array => Self::D2Array, + GPUTextureViewDimension::Cube => Self::Cube, + GPUTextureViewDimension::CubeArray => Self::CubeArray, } + } } #[derive(WebIDL)] #[webidl(enum)] pub enum GPUTextureAspect { - All, - StencilOnly, - DepthOnly, + All, + StencilOnly, + DepthOnly, } impl From for TextureAspect { - fn from(value: GPUTextureAspect) -> Self { - match value { - GPUTextureAspect::All => Self::All, - GPUTextureAspect::StencilOnly => Self::StencilOnly, - GPUTextureAspect::DepthOnly => Self::DepthOnly, - } + fn from(value: GPUTextureAspect) -> Self { + match value { + GPUTextureAspect::All => Self::All, + GPUTextureAspect::StencilOnly => Self::StencilOnly, + GPUTextureAspect::DepthOnly => Self::DepthOnly, } + } } pub struct GPUTextureView { - pub instance: Instance, - pub id: wgpu_core::id::TextureViewId, - pub label: String, + pub instance: Instance, + pub id: wgpu_core::id::TextureViewId, + pub label: String, } impl Drop for GPUTextureView { - fn drop(&mut self) { - let _ = self.instance.texture_view_drop(self.id); - } + fn drop(&mut self) { + let _ = self.instance.texture_view_drop(self.id); + } } impl WebIdlInterfaceConverter for GPUTextureView { - const NAME: &'static str = "GPUTextureView"; + const NAME: &'static str = "GPUTextureView"; } -impl GarbageCollected for GPUTextureView {} +impl GarbageCollected for GPUTextureView { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUTextureView" + } +} // TODO(@crowlKats): weakref in texture for view #[op2] impl GPUTextureView { - #[getter] - #[string] - fn label(&self) -> String { - self.label.clone() - } - #[setter] - #[string] - fn label(&self, #[webidl] _label: String) { - // TODO(@crowlKats): no-op, needs wpgu to implement changing the label - } + #[constructor] + #[cppgc] + fn constructor(_: bool) -> Result { + Err(GPUGenericError::InvalidConstructor) + } + + #[getter] + #[string] + fn label(&self) -> String { + self.label.clone() + } + #[setter] + #[string] + fn label(&self, #[webidl] _label: String) { + // TODO(@crowlKats): no-op, needs wpgu to implement changing the label + } } #[derive(WebIDL, Clone)] #[webidl(enum)] pub enum GPUTextureDimension { - #[webidl(rename = "1d")] - D1, - #[webidl(rename = "2d")] - D2, - #[webidl(rename = "3d")] - D3, + #[webidl(rename = "1d")] + D1, + #[webidl(rename = "2d")] + D2, + #[webidl(rename = "3d")] + D3, } impl From for TextureDimension { - fn from(value: GPUTextureDimension) -> Self { - match value { - GPUTextureDimension::D1 => Self::D1, - GPUTextureDimension::D2 => Self::D2, - GPUTextureDimension::D3 => Self::D3, - } + fn from(value: GPUTextureDimension) -> Self { + match value { + GPUTextureDimension::D1 => Self::D1, + GPUTextureDimension::D2 => Self::D2, + GPUTextureDimension::D3 => Self::D3, } + } } #[derive(WebIDL, Clone)] #[webidl(enum)] pub(crate) enum GPUTextureFormat { - #[webidl(rename = "r8unorm")] - R8unorm, - #[webidl(rename = "r8snorm")] - R8snorm, - #[webidl(rename = "r8uint")] - R8uint, - #[webidl(rename = "r8sint")] - R8sint, - #[webidl(rename = "r16uint")] - R16uint, - #[webidl(rename = "r16sint")] - R16sint, - #[webidl(rename = "r16float")] - R16float, - #[webidl(rename = "rg8unorm")] - Rg8unorm, - #[webidl(rename = "rg8snorm")] - Rg8snorm, - #[webidl(rename = "rg8uint")] - Rg8uint, - #[webidl(rename = "rg8sint")] - Rg8sint, - #[webidl(rename = "r32uint")] - R32uint, - #[webidl(rename = "r32sint")] - R32sint, - #[webidl(rename = "r32float")] - R32float, - #[webidl(rename = "rg16uint")] - Rg16uint, - #[webidl(rename = "rg16sint")] - Rg16sint, - #[webidl(rename = "rg16float")] - Rg16float, - #[webidl(rename = "rgba8unorm")] - Rgba8unorm, - #[webidl(rename = "rgba8unorm-srgb")] - Rgba8unormSrgb, - #[webidl(rename = "rgba8snorm")] - Rgba8snorm, - #[webidl(rename = "rgba8uint")] - Rgba8uint, - #[webidl(rename = "rgba8sint")] - Rgba8sint, - #[webidl(rename = "bgra8unorm")] - Bgra8unorm, - #[webidl(rename = "bgra8unorm-srgb")] - Bgra8unormSrgb, - #[webidl(rename = "rgb9e5ufloat")] - Rgb9e5ufloat, - #[webidl(rename = "rgb10a2uint")] - Rgb10a2uint, - #[webidl(rename = "rgb10a2unorm")] - Rgb10a2unorm, - #[webidl(rename = "rg11b10ufloat")] - Rg11b10ufloat, - #[webidl(rename = "rg32uint")] - Rg32uint, - #[webidl(rename = "rg32sint")] - Rg32sint, - #[webidl(rename = "rg32float")] - Rg32float, - #[webidl(rename = "rgba16uint")] - Rgba16uint, - #[webidl(rename = "rgba16sint")] - Rgba16sint, - #[webidl(rename = "rgba16float")] - Rgba16float, - #[webidl(rename = "rgba32uint")] - Rgba32uint, - #[webidl(rename = "rgba32sint")] - Rgba32sint, - #[webidl(rename = "rgba32float")] - Rgba32float, - #[webidl(rename = "stencil8")] - Stencil8, - #[webidl(rename = "depth16unorm")] - Depth16unorm, - #[webidl(rename = "depth24plus")] - Depth24plus, - #[webidl(rename = "depth24plus-stencil8")] - Depth24plusStencil8, - #[webidl(rename = "depth32float")] - Depth32float, - #[webidl(rename = "depth32float-stencil8")] - Depth32floatStencil8, - #[webidl(rename = "bc1-rgba-unorm")] - Bc1RgbaUnorm, - #[webidl(rename = "bc1-rgba-unorm-srgb")] - Bc1RgbaUnormSrgb, - #[webidl(rename = "bc2-rgba-unorm")] - Bc2RgbaUnorm, - #[webidl(rename = "bc2-rgba-unorm-srgb")] - Bc2RgbaUnormSrgb, - #[webidl(rename = "bc3-rgba-unorm")] - Bc3RgbaUnorm, - #[webidl(rename = "bc3-rgba-unorm-srgb")] - Bc3RgbaUnormSrgb, - #[webidl(rename = "bc4-r-unorm")] - Bc4RUnorm, - #[webidl(rename = "bc4-r-snorm")] - Bc4RSnorm, - #[webidl(rename = "bc5-rg-unorm")] - Bc5RgUnorm, - #[webidl(rename = "bc5-rg-snorm")] - Bc5RgSnorm, - #[webidl(rename = "bc6h-rgb-ufloat")] - Bc6hRgbUfloat, - #[webidl(rename = "bc6h-rgb-float")] - Bc6hRgbFloat, - #[webidl(rename = "bc7-rgba-unorm")] - Bc7RgbaUnorm, - #[webidl(rename = "bc7-rgba-unorm-srgb")] - Bc7RgbaUnormSrgb, - #[webidl(rename = "etc2-rgb8unorm")] - Etc2Rgb8unorm, - #[webidl(rename = "etc2-rgb8unorm-srgb")] - Etc2Rgb8unormSrgb, - #[webidl(rename = "etc2-rgb8a1unorm")] - Etc2Rgb8a1unorm, - #[webidl(rename = "etc2-rgb8a1unorm-srgb")] - Etc2Rgb8a1unormSrgb, - #[webidl(rename = "etc2-rgba8unorm")] - Etc2Rgba8unorm, - #[webidl(rename = "etc2-rgba8unorm-srgb")] - Etc2Rgba8unormSrgb, - #[webidl(rename = "eac-r11unorm")] - EacR11unorm, - #[webidl(rename = "eac-r11snorm")] - EacR11snorm, - #[webidl(rename = "eac-rg11unorm")] - EacRg11unorm, - #[webidl(rename = "eac-rg11snorm")] - EacRg11snorm, - #[webidl(rename = "astc-4x4-unorm")] - Astc4x4Unorm, - #[webidl(rename = "astc-4x4-unorm-srgb")] - Astc4x4UnormSrgb, - #[webidl(rename = "astc-5x4-unorm")] - Astc5x4Unorm, - #[webidl(rename = "astc-5x4-unorm-srgb")] - Astc5x4UnormSrgb, - #[webidl(rename = "astc-5x5-unorm")] - Astc5x5Unorm, - #[webidl(rename = "astc-5x5-unorm-srgb")] - Astc5x5UnormSrgb, - #[webidl(rename = "astc-6x5-unorm")] - Astc6x5Unorm, - #[webidl(rename = "astc-6x5-unorm-srgb")] - Astc6x5UnormSrgb, - #[webidl(rename = "astc-6x6-unorm")] - Astc6x6Unorm, - #[webidl(rename = "astc-6x6-unorm-srgb")] - Astc6x6UnormSrgb, - #[webidl(rename = "astc-8x5-unorm")] - Astc8x5Unorm, - #[webidl(rename = "astc-8x5-unorm-srgb")] - Astc8x5UnormSrgb, - #[webidl(rename = "astc-8x6-unorm")] - Astc8x6Unorm, - #[webidl(rename = "astc-8x6-unorm-srgb")] - Astc8x6UnormSrgb, - #[webidl(rename = "astc-8x8-unorm")] - Astc8x8Unorm, - #[webidl(rename = "astc-8x8-unorm-srgb")] - Astc8x8UnormSrgb, - #[webidl(rename = "astc-10x5-unorm")] - Astc10x5Unorm, - #[webidl(rename = "astc-10x5-unorm-srgb")] - Astc10x5UnormSrgb, - #[webidl(rename = "astc-10x6-unorm")] - Astc10x6Unorm, - #[webidl(rename = "astc-10x6-unorm-srgb")] - Astc10x6UnormSrgb, - #[webidl(rename = "astc-10x8-unorm")] - Astc10x8Unorm, - #[webidl(rename = "astc-10x8-unorm-srgb")] - Astc10x8UnormSrgb, - #[webidl(rename = "astc-10x10-unorm")] - Astc10x10Unorm, - #[webidl(rename = "astc-10x10-unorm-srgb")] - Astc10x10UnormSrgb, - #[webidl(rename = "astc-12x10-unorm")] - Astc12x10Unorm, - #[webidl(rename = "astc-12x10-unorm-srgb")] - Astc12x10UnormSrgb, - #[webidl(rename = "astc-12x12-unorm")] - Astc12x12Unorm, - #[webidl(rename = "astc-12x12-unorm-srgb")] - Astc12x12UnormSrgb, + #[webidl(rename = "r8unorm")] + R8unorm, + #[webidl(rename = "r8snorm")] + R8snorm, + #[webidl(rename = "r8uint")] + R8uint, + #[webidl(rename = "r8sint")] + R8sint, + #[webidl(rename = "r16uint")] + R16uint, + #[webidl(rename = "r16sint")] + R16sint, + #[webidl(rename = "r16float")] + R16float, + #[webidl(rename = "rg8unorm")] + Rg8unorm, + #[webidl(rename = "rg8snorm")] + Rg8snorm, + #[webidl(rename = "rg8uint")] + Rg8uint, + #[webidl(rename = "rg8sint")] + Rg8sint, + #[webidl(rename = "r32uint")] + R32uint, + #[webidl(rename = "r32sint")] + R32sint, + #[webidl(rename = "r32float")] + R32float, + #[webidl(rename = "rg16uint")] + Rg16uint, + #[webidl(rename = "rg16sint")] + Rg16sint, + #[webidl(rename = "rg16float")] + Rg16float, + #[webidl(rename = "rgba8unorm")] + Rgba8unorm, + #[webidl(rename = "rgba8unorm-srgb")] + Rgba8unormSrgb, + #[webidl(rename = "rgba8snorm")] + Rgba8snorm, + #[webidl(rename = "rgba8uint")] + Rgba8uint, + #[webidl(rename = "rgba8sint")] + Rgba8sint, + #[webidl(rename = "bgra8unorm")] + Bgra8unorm, + #[webidl(rename = "bgra8unorm-srgb")] + Bgra8unormSrgb, + #[webidl(rename = "rgb9e5ufloat")] + Rgb9e5ufloat, + #[webidl(rename = "rgb10a2uint")] + Rgb10a2uint, + #[webidl(rename = "rgb10a2unorm")] + Rgb10a2unorm, + #[webidl(rename = "rg11b10ufloat")] + Rg11b10ufloat, + #[webidl(rename = "rg32uint")] + Rg32uint, + #[webidl(rename = "rg32sint")] + Rg32sint, + #[webidl(rename = "rg32float")] + Rg32float, + #[webidl(rename = "rgba16uint")] + Rgba16uint, + #[webidl(rename = "rgba16sint")] + Rgba16sint, + #[webidl(rename = "rgba16float")] + Rgba16float, + #[webidl(rename = "rgba32uint")] + Rgba32uint, + #[webidl(rename = "rgba32sint")] + Rgba32sint, + #[webidl(rename = "rgba32float")] + Rgba32float, + #[webidl(rename = "stencil8")] + Stencil8, + #[webidl(rename = "depth16unorm")] + Depth16unorm, + #[webidl(rename = "depth24plus")] + Depth24plus, + #[webidl(rename = "depth24plus-stencil8")] + Depth24plusStencil8, + #[webidl(rename = "depth32float")] + Depth32float, + #[webidl(rename = "depth32float-stencil8")] + Depth32floatStencil8, + #[webidl(rename = "bc1-rgba-unorm")] + Bc1RgbaUnorm, + #[webidl(rename = "bc1-rgba-unorm-srgb")] + Bc1RgbaUnormSrgb, + #[webidl(rename = "bc2-rgba-unorm")] + Bc2RgbaUnorm, + #[webidl(rename = "bc2-rgba-unorm-srgb")] + Bc2RgbaUnormSrgb, + #[webidl(rename = "bc3-rgba-unorm")] + Bc3RgbaUnorm, + #[webidl(rename = "bc3-rgba-unorm-srgb")] + Bc3RgbaUnormSrgb, + #[webidl(rename = "bc4-r-unorm")] + Bc4RUnorm, + #[webidl(rename = "bc4-r-snorm")] + Bc4RSnorm, + #[webidl(rename = "bc5-rg-unorm")] + Bc5RgUnorm, + #[webidl(rename = "bc5-rg-snorm")] + Bc5RgSnorm, + #[webidl(rename = "bc6h-rgb-ufloat")] + Bc6hRgbUfloat, + #[webidl(rename = "bc6h-rgb-float")] + Bc6hRgbFloat, + #[webidl(rename = "bc7-rgba-unorm")] + Bc7RgbaUnorm, + #[webidl(rename = "bc7-rgba-unorm-srgb")] + Bc7RgbaUnormSrgb, + #[webidl(rename = "etc2-rgb8unorm")] + Etc2Rgb8unorm, + #[webidl(rename = "etc2-rgb8unorm-srgb")] + Etc2Rgb8unormSrgb, + #[webidl(rename = "etc2-rgb8a1unorm")] + Etc2Rgb8a1unorm, + #[webidl(rename = "etc2-rgb8a1unorm-srgb")] + Etc2Rgb8a1unormSrgb, + #[webidl(rename = "etc2-rgba8unorm")] + Etc2Rgba8unorm, + #[webidl(rename = "etc2-rgba8unorm-srgb")] + Etc2Rgba8unormSrgb, + #[webidl(rename = "eac-r11unorm")] + EacR11unorm, + #[webidl(rename = "eac-r11snorm")] + EacR11snorm, + #[webidl(rename = "eac-rg11unorm")] + EacRg11unorm, + #[webidl(rename = "eac-rg11snorm")] + EacRg11snorm, + #[webidl(rename = "astc-4x4-unorm")] + Astc4x4Unorm, + #[webidl(rename = "astc-4x4-unorm-srgb")] + Astc4x4UnormSrgb, + #[webidl(rename = "astc-5x4-unorm")] + Astc5x4Unorm, + #[webidl(rename = "astc-5x4-unorm-srgb")] + Astc5x4UnormSrgb, + #[webidl(rename = "astc-5x5-unorm")] + Astc5x5Unorm, + #[webidl(rename = "astc-5x5-unorm-srgb")] + Astc5x5UnormSrgb, + #[webidl(rename = "astc-6x5-unorm")] + Astc6x5Unorm, + #[webidl(rename = "astc-6x5-unorm-srgb")] + Astc6x5UnormSrgb, + #[webidl(rename = "astc-6x6-unorm")] + Astc6x6Unorm, + #[webidl(rename = "astc-6x6-unorm-srgb")] + Astc6x6UnormSrgb, + #[webidl(rename = "astc-8x5-unorm")] + Astc8x5Unorm, + #[webidl(rename = "astc-8x5-unorm-srgb")] + Astc8x5UnormSrgb, + #[webidl(rename = "astc-8x6-unorm")] + Astc8x6Unorm, + #[webidl(rename = "astc-8x6-unorm-srgb")] + Astc8x6UnormSrgb, + #[webidl(rename = "astc-8x8-unorm")] + Astc8x8Unorm, + #[webidl(rename = "astc-8x8-unorm-srgb")] + Astc8x8UnormSrgb, + #[webidl(rename = "astc-10x5-unorm")] + Astc10x5Unorm, + #[webidl(rename = "astc-10x5-unorm-srgb")] + Astc10x5UnormSrgb, + #[webidl(rename = "astc-10x6-unorm")] + Astc10x6Unorm, + #[webidl(rename = "astc-10x6-unorm-srgb")] + Astc10x6UnormSrgb, + #[webidl(rename = "astc-10x8-unorm")] + Astc10x8Unorm, + #[webidl(rename = "astc-10x8-unorm-srgb")] + Astc10x8UnormSrgb, + #[webidl(rename = "astc-10x10-unorm")] + Astc10x10Unorm, + #[webidl(rename = "astc-10x10-unorm-srgb")] + Astc10x10UnormSrgb, + #[webidl(rename = "astc-12x10-unorm")] + Astc12x10Unorm, + #[webidl(rename = "astc-12x10-unorm-srgb")] + Astc12x10UnormSrgb, + #[webidl(rename = "astc-12x12-unorm")] + Astc12x12Unorm, + #[webidl(rename = "astc-12x12-unorm-srgb")] + Astc12x12UnormSrgb, } impl From for TextureFormat { - fn from(value: GPUTextureFormat) -> Self { - match value { - GPUTextureFormat::R8unorm => Self::R8Unorm, - GPUTextureFormat::R8snorm => Self::R8Snorm, - GPUTextureFormat::R8uint => Self::R8Uint, - GPUTextureFormat::R8sint => Self::R8Sint, - GPUTextureFormat::R16uint => Self::R16Uint, - GPUTextureFormat::R16sint => Self::R16Sint, - GPUTextureFormat::R16float => Self::R16Float, - GPUTextureFormat::Rg8unorm => Self::Rg8Unorm, - GPUTextureFormat::Rg8snorm => Self::Rg8Snorm, - GPUTextureFormat::Rg8uint => Self::Rg8Uint, - GPUTextureFormat::Rg8sint => Self::Rg8Sint, - GPUTextureFormat::R32uint => Self::R32Uint, - GPUTextureFormat::R32sint => Self::R32Sint, - GPUTextureFormat::R32float => Self::R32Float, - GPUTextureFormat::Rg16uint => Self::Rg16Uint, - GPUTextureFormat::Rg16sint => Self::Rg16Sint, - GPUTextureFormat::Rg16float => Self::Rg16Float, - GPUTextureFormat::Rgba8unorm => Self::Rgba8Unorm, - GPUTextureFormat::Rgba8unormSrgb => Self::Rgba8UnormSrgb, - GPUTextureFormat::Rgba8snorm => Self::Rgba8Snorm, - GPUTextureFormat::Rgba8uint => Self::Rgba8Uint, - GPUTextureFormat::Rgba8sint => Self::Rgba8Sint, - GPUTextureFormat::Bgra8unorm => Self::Bgra8Unorm, - GPUTextureFormat::Bgra8unormSrgb => Self::Bgra8UnormSrgb, - GPUTextureFormat::Rgb9e5ufloat => Self::Rgb9e5Ufloat, - GPUTextureFormat::Rgb10a2uint => Self::Rgb10a2Uint, - GPUTextureFormat::Rgb10a2unorm => Self::Rgb10a2Unorm, - GPUTextureFormat::Rg11b10ufloat => Self::Rg11b10Ufloat, - GPUTextureFormat::Rg32uint => Self::Rg32Uint, - GPUTextureFormat::Rg32sint => Self::Rg32Sint, - GPUTextureFormat::Rg32float => Self::Rg32Float, - GPUTextureFormat::Rgba16uint => Self::Rgba16Uint, - GPUTextureFormat::Rgba16sint => Self::Rgba16Sint, - GPUTextureFormat::Rgba16float => Self::Rgba16Float, - GPUTextureFormat::Rgba32uint => Self::Rgba32Uint, - GPUTextureFormat::Rgba32sint => Self::Rgba32Sint, - GPUTextureFormat::Rgba32float => Self::Rgba32Float, - GPUTextureFormat::Stencil8 => Self::Stencil8, - GPUTextureFormat::Depth16unorm => Self::Depth16Unorm, - GPUTextureFormat::Depth24plus => Self::Depth24Plus, - GPUTextureFormat::Depth24plusStencil8 => Self::Depth24PlusStencil8, - GPUTextureFormat::Depth32float => Self::Depth32Float, - GPUTextureFormat::Depth32floatStencil8 => Self::Depth32FloatStencil8, - GPUTextureFormat::Bc1RgbaUnorm => Self::Bc1RgbaUnorm, - GPUTextureFormat::Bc1RgbaUnormSrgb => Self::Bc1RgbaUnormSrgb, - GPUTextureFormat::Bc2RgbaUnorm => Self::Bc2RgbaUnorm, - GPUTextureFormat::Bc2RgbaUnormSrgb => Self::Bc2RgbaUnormSrgb, - GPUTextureFormat::Bc3RgbaUnorm => Self::Bc3RgbaUnorm, - GPUTextureFormat::Bc3RgbaUnormSrgb => Self::Bc3RgbaUnormSrgb, - GPUTextureFormat::Bc4RUnorm => Self::Bc4RUnorm, - GPUTextureFormat::Bc4RSnorm => Self::Bc4RSnorm, - GPUTextureFormat::Bc5RgUnorm => Self::Bc5RgUnorm, - GPUTextureFormat::Bc5RgSnorm => Self::Bc5RgSnorm, - GPUTextureFormat::Bc6hRgbUfloat => Self::Bc6hRgbUfloat, - GPUTextureFormat::Bc6hRgbFloat => Self::Bc6hRgbFloat, - GPUTextureFormat::Bc7RgbaUnorm => Self::Bc7RgbaUnorm, - GPUTextureFormat::Bc7RgbaUnormSrgb => Self::Bc7RgbaUnormSrgb, - GPUTextureFormat::Etc2Rgb8unorm => Self::Etc2Rgb8Unorm, - GPUTextureFormat::Etc2Rgb8unormSrgb => Self::Etc2Rgb8UnormSrgb, - GPUTextureFormat::Etc2Rgb8a1unorm => Self::Etc2Rgb8A1Unorm, - GPUTextureFormat::Etc2Rgb8a1unormSrgb => Self::Etc2Rgb8A1UnormSrgb, - GPUTextureFormat::Etc2Rgba8unorm => Self::Etc2Rgba8Unorm, - GPUTextureFormat::Etc2Rgba8unormSrgb => Self::Etc2Rgba8UnormSrgb, - GPUTextureFormat::EacR11unorm => Self::EacR11Unorm, - GPUTextureFormat::EacR11snorm => Self::EacR11Snorm, - GPUTextureFormat::EacRg11unorm => Self::EacRg11Unorm, - GPUTextureFormat::EacRg11snorm => Self::EacRg11Snorm, - GPUTextureFormat::Astc4x4Unorm => Self::Astc { - block: AstcBlock::B4x4, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc4x4UnormSrgb => Self::Astc { - block: AstcBlock::B4x4, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc5x4Unorm => Self::Astc { - block: AstcBlock::B5x4, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc5x4UnormSrgb => Self::Astc { - block: AstcBlock::B5x4, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc5x5Unorm => Self::Astc { - block: AstcBlock::B5x5, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc5x5UnormSrgb => Self::Astc { - block: AstcBlock::B5x5, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc6x5Unorm => Self::Astc { - block: AstcBlock::B6x5, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc6x5UnormSrgb => Self::Astc { - block: AstcBlock::B6x5, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc6x6Unorm => Self::Astc { - block: AstcBlock::B6x6, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc6x6UnormSrgb => Self::Astc { - block: AstcBlock::B6x6, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc8x5Unorm => Self::Astc { - block: AstcBlock::B8x5, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc8x5UnormSrgb => Self::Astc { - block: AstcBlock::B8x5, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc8x6Unorm => Self::Astc { - block: AstcBlock::B8x6, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc8x6UnormSrgb => Self::Astc { - block: AstcBlock::B8x6, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc8x8Unorm => Self::Astc { - block: AstcBlock::B8x8, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc8x8UnormSrgb => Self::Astc { - block: AstcBlock::B8x8, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc10x5Unorm => Self::Astc { - block: AstcBlock::B10x5, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc10x5UnormSrgb => Self::Astc { - block: AstcBlock::B10x5, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc10x6Unorm => Self::Astc { - block: AstcBlock::B10x6, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc10x6UnormSrgb => Self::Astc { - block: AstcBlock::B10x6, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc10x8Unorm => Self::Astc { - block: AstcBlock::B10x8, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc10x8UnormSrgb => Self::Astc { - block: AstcBlock::B10x8, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc10x10Unorm => Self::Astc { - block: AstcBlock::B10x10, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc10x10UnormSrgb => Self::Astc { - block: AstcBlock::B10x10, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc12x10Unorm => Self::Astc { - block: AstcBlock::B12x10, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc12x10UnormSrgb => Self::Astc { - block: AstcBlock::B12x10, - channel: AstcChannel::UnormSrgb, - }, - GPUTextureFormat::Astc12x12Unorm => Self::Astc { - block: AstcBlock::B12x12, - channel: AstcChannel::Unorm, - }, - GPUTextureFormat::Astc12x12UnormSrgb => Self::Astc { - block: AstcBlock::B12x12, - channel: AstcChannel::UnormSrgb, - }, - } + fn from(value: GPUTextureFormat) -> Self { + match value { + GPUTextureFormat::R8unorm => Self::R8Unorm, + GPUTextureFormat::R8snorm => Self::R8Snorm, + GPUTextureFormat::R8uint => Self::R8Uint, + GPUTextureFormat::R8sint => Self::R8Sint, + GPUTextureFormat::R16uint => Self::R16Uint, + GPUTextureFormat::R16sint => Self::R16Sint, + GPUTextureFormat::R16float => Self::R16Float, + GPUTextureFormat::Rg8unorm => Self::Rg8Unorm, + GPUTextureFormat::Rg8snorm => Self::Rg8Snorm, + GPUTextureFormat::Rg8uint => Self::Rg8Uint, + GPUTextureFormat::Rg8sint => Self::Rg8Sint, + GPUTextureFormat::R32uint => Self::R32Uint, + GPUTextureFormat::R32sint => Self::R32Sint, + GPUTextureFormat::R32float => Self::R32Float, + GPUTextureFormat::Rg16uint => Self::Rg16Uint, + GPUTextureFormat::Rg16sint => Self::Rg16Sint, + GPUTextureFormat::Rg16float => Self::Rg16Float, + GPUTextureFormat::Rgba8unorm => Self::Rgba8Unorm, + GPUTextureFormat::Rgba8unormSrgb => Self::Rgba8UnormSrgb, + GPUTextureFormat::Rgba8snorm => Self::Rgba8Snorm, + GPUTextureFormat::Rgba8uint => Self::Rgba8Uint, + GPUTextureFormat::Rgba8sint => Self::Rgba8Sint, + GPUTextureFormat::Bgra8unorm => Self::Bgra8Unorm, + GPUTextureFormat::Bgra8unormSrgb => Self::Bgra8UnormSrgb, + GPUTextureFormat::Rgb9e5ufloat => Self::Rgb9e5Ufloat, + GPUTextureFormat::Rgb10a2uint => Self::Rgb10a2Uint, + GPUTextureFormat::Rgb10a2unorm => Self::Rgb10a2Unorm, + GPUTextureFormat::Rg11b10ufloat => Self::Rg11b10Ufloat, + GPUTextureFormat::Rg32uint => Self::Rg32Uint, + GPUTextureFormat::Rg32sint => Self::Rg32Sint, + GPUTextureFormat::Rg32float => Self::Rg32Float, + GPUTextureFormat::Rgba16uint => Self::Rgba16Uint, + GPUTextureFormat::Rgba16sint => Self::Rgba16Sint, + GPUTextureFormat::Rgba16float => Self::Rgba16Float, + GPUTextureFormat::Rgba32uint => Self::Rgba32Uint, + GPUTextureFormat::Rgba32sint => Self::Rgba32Sint, + GPUTextureFormat::Rgba32float => Self::Rgba32Float, + GPUTextureFormat::Stencil8 => Self::Stencil8, + GPUTextureFormat::Depth16unorm => Self::Depth16Unorm, + GPUTextureFormat::Depth24plus => Self::Depth24Plus, + GPUTextureFormat::Depth24plusStencil8 => Self::Depth24PlusStencil8, + GPUTextureFormat::Depth32float => Self::Depth32Float, + GPUTextureFormat::Depth32floatStencil8 => Self::Depth32FloatStencil8, + GPUTextureFormat::Bc1RgbaUnorm => Self::Bc1RgbaUnorm, + GPUTextureFormat::Bc1RgbaUnormSrgb => Self::Bc1RgbaUnormSrgb, + GPUTextureFormat::Bc2RgbaUnorm => Self::Bc2RgbaUnorm, + GPUTextureFormat::Bc2RgbaUnormSrgb => Self::Bc2RgbaUnormSrgb, + GPUTextureFormat::Bc3RgbaUnorm => Self::Bc3RgbaUnorm, + GPUTextureFormat::Bc3RgbaUnormSrgb => Self::Bc3RgbaUnormSrgb, + GPUTextureFormat::Bc4RUnorm => Self::Bc4RUnorm, + GPUTextureFormat::Bc4RSnorm => Self::Bc4RSnorm, + GPUTextureFormat::Bc5RgUnorm => Self::Bc5RgUnorm, + GPUTextureFormat::Bc5RgSnorm => Self::Bc5RgSnorm, + GPUTextureFormat::Bc6hRgbUfloat => Self::Bc6hRgbUfloat, + GPUTextureFormat::Bc6hRgbFloat => Self::Bc6hRgbFloat, + GPUTextureFormat::Bc7RgbaUnorm => Self::Bc7RgbaUnorm, + GPUTextureFormat::Bc7RgbaUnormSrgb => Self::Bc7RgbaUnormSrgb, + GPUTextureFormat::Etc2Rgb8unorm => Self::Etc2Rgb8Unorm, + GPUTextureFormat::Etc2Rgb8unormSrgb => Self::Etc2Rgb8UnormSrgb, + GPUTextureFormat::Etc2Rgb8a1unorm => Self::Etc2Rgb8A1Unorm, + GPUTextureFormat::Etc2Rgb8a1unormSrgb => Self::Etc2Rgb8A1UnormSrgb, + GPUTextureFormat::Etc2Rgba8unorm => Self::Etc2Rgba8Unorm, + GPUTextureFormat::Etc2Rgba8unormSrgb => Self::Etc2Rgba8UnormSrgb, + GPUTextureFormat::EacR11unorm => Self::EacR11Unorm, + GPUTextureFormat::EacR11snorm => Self::EacR11Snorm, + GPUTextureFormat::EacRg11unorm => Self::EacRg11Unorm, + GPUTextureFormat::EacRg11snorm => Self::EacRg11Snorm, + GPUTextureFormat::Astc4x4Unorm => Self::Astc { + block: AstcBlock::B4x4, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc4x4UnormSrgb => Self::Astc { + block: AstcBlock::B4x4, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc5x4Unorm => Self::Astc { + block: AstcBlock::B5x4, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc5x4UnormSrgb => Self::Astc { + block: AstcBlock::B5x4, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc5x5Unorm => Self::Astc { + block: AstcBlock::B5x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc5x5UnormSrgb => Self::Astc { + block: AstcBlock::B5x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc6x5Unorm => Self::Astc { + block: AstcBlock::B6x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc6x5UnormSrgb => Self::Astc { + block: AstcBlock::B6x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc6x6Unorm => Self::Astc { + block: AstcBlock::B6x6, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc6x6UnormSrgb => Self::Astc { + block: AstcBlock::B6x6, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc8x5Unorm => Self::Astc { + block: AstcBlock::B8x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc8x5UnormSrgb => Self::Astc { + block: AstcBlock::B8x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc8x6Unorm => Self::Astc { + block: AstcBlock::B8x6, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc8x6UnormSrgb => Self::Astc { + block: AstcBlock::B8x6, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc8x8Unorm => Self::Astc { + block: AstcBlock::B8x8, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc8x8UnormSrgb => Self::Astc { + block: AstcBlock::B8x8, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x5Unorm => Self::Astc { + block: AstcBlock::B10x5, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x5UnormSrgb => Self::Astc { + block: AstcBlock::B10x5, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x6Unorm => Self::Astc { + block: AstcBlock::B10x6, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x6UnormSrgb => Self::Astc { + block: AstcBlock::B10x6, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x8Unorm => Self::Astc { + block: AstcBlock::B10x8, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x8UnormSrgb => Self::Astc { + block: AstcBlock::B10x8, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc10x10Unorm => Self::Astc { + block: AstcBlock::B10x10, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc10x10UnormSrgb => Self::Astc { + block: AstcBlock::B10x10, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc12x10Unorm => Self::Astc { + block: AstcBlock::B12x10, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc12x10UnormSrgb => Self::Astc { + block: AstcBlock::B12x10, + channel: AstcChannel::UnormSrgb, + }, + GPUTextureFormat::Astc12x12Unorm => Self::Astc { + block: AstcBlock::B12x12, + channel: AstcChannel::Unorm, + }, + GPUTextureFormat::Astc12x12UnormSrgb => Self::Astc { + block: AstcBlock::B12x12, + channel: AstcChannel::UnormSrgb, + }, } + } } pub struct GPUExternalTexture {} impl WebIdlInterfaceConverter for GPUExternalTexture { - const NAME: &'static str = "GPUExternalTexture"; + const NAME: &'static str = "GPUExternalTexture"; } -impl GarbageCollected for GPUExternalTexture {} +impl GarbageCollected for GPUExternalTexture { + fn get_name(&self) -> &'static std::ffi::CStr { + c"GPUExternalTexture" + } +} #[op2] impl GPUExternalTexture {} diff --git a/deno_webgpu/webgpu.idl b/deno_webgpu/webgpu.idl deleted file mode 100644 index cb4fff59655..00000000000 --- a/deno_webgpu/webgpu.idl +++ /dev/null @@ -1,1244 +0,0 @@ -interface mixin GPUObjectBase { - attribute USVString label; -}; - -dictionary GPUObjectDescriptorBase { - USVString label = ""; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUSupportedLimits { - readonly attribute unsigned long maxTextureDimension1D; - readonly attribute unsigned long maxTextureDimension2D; - readonly attribute unsigned long maxTextureDimension3D; - readonly attribute unsigned long maxTextureArrayLayers; - readonly attribute unsigned long maxBindGroups; - readonly attribute unsigned long maxBindingsPerBindGroup; - readonly attribute unsigned long maxDynamicUniformBuffersPerPipelineLayout; - readonly attribute unsigned long maxDynamicStorageBuffersPerPipelineLayout; - readonly attribute unsigned long maxSampledTexturesPerShaderStage; - readonly attribute unsigned long maxSamplersPerShaderStage; - readonly attribute unsigned long maxStorageBuffersPerShaderStage; - readonly attribute unsigned long maxStorageTexturesPerShaderStage; - readonly attribute unsigned long maxUniformBuffersPerShaderStage; - readonly attribute unsigned long long maxUniformBufferBindingSize; - readonly attribute unsigned long long maxStorageBufferBindingSize; - readonly attribute unsigned long minUniformBufferOffsetAlignment; - readonly attribute unsigned long minStorageBufferOffsetAlignment; - readonly attribute unsigned long maxVertexBuffers; - readonly attribute unsigned long long maxBufferSize; - readonly attribute unsigned long maxVertexAttributes; - readonly attribute unsigned long maxVertexBufferArrayStride; - readonly attribute unsigned long maxColorAttachments; - readonly attribute unsigned long maxColorAttachmentBytesPerSample; - readonly attribute unsigned long maxComputeWorkgroupStorageSize; - readonly attribute unsigned long maxComputeInvocationsPerWorkgroup; - readonly attribute unsigned long maxComputeWorkgroupSizeX; - readonly attribute unsigned long maxComputeWorkgroupSizeY; - readonly attribute unsigned long maxComputeWorkgroupSizeZ; - readonly attribute unsigned long maxComputeWorkgroupsPerDimension; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUSupportedFeatures { - readonly setlike; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUAdapterInfo { - readonly attribute DOMString vendor; - readonly attribute DOMString architecture; - readonly attribute DOMString device; - readonly attribute DOMString description; - readonly attribute unsigned long subgroupMinSize; - readonly attribute unsigned long subgroupMaxSize; -}; - -interface mixin NavigatorGPU { - [SameObject, SecureContext] readonly attribute GPU gpu; -}; -Navigator includes NavigatorGPU; -WorkerNavigator includes NavigatorGPU; - -[Exposed=(Window, Worker), SecureContext] -interface GPU { - Promise requestAdapter(optional GPURequestAdapterOptions options = {}); - GPUTextureFormat getPreferredCanvasFormat(); -}; - -dictionary GPURequestAdapterOptions { - GPUPowerPreference powerPreference; - boolean forceFallbackAdapter = false; -}; - -enum GPUPowerPreference { - "low-power", - "high-performance", -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUAdapter { - [SameObject] readonly attribute GPUSupportedFeatures features; - [SameObject] readonly attribute GPUSupportedLimits limits; - [SameObject] readonly attribute GPUAdapterInfo info; - readonly attribute boolean isFallbackAdapter; - - Promise requestDevice(optional GPUDeviceDescriptor descriptor = {}); -}; - -dictionary GPUDeviceDescriptor - : GPUObjectDescriptorBase { - sequence requiredFeatures = []; - record requiredLimits = {}; -}; - -enum GPUFeatureName { - "depth-clip-control", - "depth32float-stencil8", - "texture-compression-bc", - "texture-compression-bc-sliced-3d", - "texture-compression-etc2", - "texture-compression-astc", - "texture-compression-astc-sliced-3d", - "timestamp-query", - "indirect-first-instance", - "shader-f16", - "rg11b10ufloat-renderable", - "bgra8unorm-storage", - "float32-filterable", - "subgroups", -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUDevice : EventTarget { - [SameObject] readonly attribute GPUSupportedFeatures features; - [SameObject] readonly attribute GPUSupportedLimits limits; - [SameObject] readonly attribute GPUAdapterInfo adapterInfo; - - [SameObject] readonly attribute GPUQueue queue; - - undefined destroy(); - - GPUBuffer createBuffer(GPUBufferDescriptor descriptor); - GPUTexture createTexture(GPUTextureDescriptor descriptor); - GPUSampler createSampler(optional GPUSamplerDescriptor descriptor = {}); - - GPUBindGroupLayout createBindGroupLayout(GPUBindGroupLayoutDescriptor descriptor); - GPUPipelineLayout createPipelineLayout(GPUPipelineLayoutDescriptor descriptor); - GPUBindGroup createBindGroup(GPUBindGroupDescriptor descriptor); - - GPUShaderModule createShaderModule(GPUShaderModuleDescriptor descriptor); - GPUComputePipeline createComputePipeline(GPUComputePipelineDescriptor descriptor); - GPURenderPipeline createRenderPipeline(GPURenderPipelineDescriptor descriptor); - Promise createComputePipelineAsync(GPUComputePipelineDescriptor descriptor); - Promise createRenderPipelineAsync(GPURenderPipelineDescriptor descriptor); - - GPUCommandEncoder createCommandEncoder(optional GPUCommandEncoderDescriptor descriptor = {}); - GPURenderBundleEncoder createRenderBundleEncoder(GPURenderBundleEncoderDescriptor descriptor); - - GPUQuerySet createQuerySet(GPUQuerySetDescriptor descriptor); -}; -GPUDevice includes GPUObjectBase; - -[Exposed=(Window, Worker), SecureContext] -interface GPUBuffer { - readonly attribute GPUSize64Out size; - readonly attribute GPUFlagsConstant usage; - - readonly attribute GPUBufferMapState mapState; - - Promise mapAsync(GPUMapModeFlags mode, optional GPUSize64 offset = 0, optional GPUSize64 size); - ArrayBuffer getMappedRange(optional GPUSize64 offset = 0, optional GPUSize64 size); - undefined unmap(); - - undefined destroy(); -}; -GPUBuffer includes GPUObjectBase; - -enum GPUBufferMapState { - "unmapped", - "pending", - "mapped", -}; - -dictionary GPUBufferDescriptor - : GPUObjectDescriptorBase { - required GPUSize64 size; - required GPUBufferUsageFlags usage; - boolean mappedAtCreation = false; -}; - -typedef [EnforceRange] unsigned long GPUBufferUsageFlags; -[Exposed=(Window, Worker), SecureContext] -namespace GPUBufferUsage { - const GPUFlagsConstant MAP_READ = 0x0001; - const GPUFlagsConstant MAP_WRITE = 0x0002; - const GPUFlagsConstant COPY_SRC = 0x0004; - const GPUFlagsConstant COPY_DST = 0x0008; - const GPUFlagsConstant INDEX = 0x0010; - const GPUFlagsConstant VERTEX = 0x0020; - const GPUFlagsConstant UNIFORM = 0x0040; - const GPUFlagsConstant STORAGE = 0x0080; - const GPUFlagsConstant INDIRECT = 0x0100; - const GPUFlagsConstant QUERY_RESOLVE = 0x0200; -}; - -typedef [EnforceRange] unsigned long GPUMapModeFlags; -[Exposed=(Window, Worker), SecureContext] -namespace GPUMapMode { - const GPUFlagsConstant READ = 0x0001; - const GPUFlagsConstant WRITE = 0x0002; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUTexture { - GPUTextureView createView(optional GPUTextureViewDescriptor descriptor = {}); - - undefined destroy(); - - readonly attribute GPUIntegerCoordinateOut width; - readonly attribute GPUIntegerCoordinateOut height; - readonly attribute GPUIntegerCoordinateOut depthOrArrayLayers; - readonly attribute GPUIntegerCoordinateOut mipLevelCount; - readonly attribute GPUSize32Out sampleCount; - readonly attribute GPUTextureDimension dimension; - readonly attribute GPUTextureFormat format; - readonly attribute GPUFlagsConstant usage; -}; -GPUTexture includes GPUObjectBase; - -dictionary GPUTextureDescriptor - : GPUObjectDescriptorBase { - required GPUExtent3D size; - GPUIntegerCoordinate mipLevelCount = 1; - GPUSize32 sampleCount = 1; - GPUTextureDimension dimension = "2d"; - required GPUTextureFormat format; - required GPUTextureUsageFlags usage; - sequence viewFormats = []; -}; - -enum GPUTextureDimension { - "1d", - "2d", - "3d", -}; - -typedef [EnforceRange] unsigned long GPUTextureUsageFlags; -[Exposed=(Window, Worker), SecureContext] -namespace GPUTextureUsage { - const GPUFlagsConstant COPY_SRC = 0x01; - const GPUFlagsConstant COPY_DST = 0x02; - const GPUFlagsConstant TEXTURE_BINDING = 0x04; - const GPUFlagsConstant STORAGE_BINDING = 0x08; - const GPUFlagsConstant RENDER_ATTACHMENT = 0x10; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUTextureView { -}; -GPUTextureView includes GPUObjectBase; - -dictionary GPUTextureViewDescriptor - : GPUObjectDescriptorBase { - GPUTextureFormat format; - GPUTextureViewDimension dimension; - GPUTextureUsageFlags usage = 0; - GPUTextureAspect aspect = "all"; - GPUIntegerCoordinate baseMipLevel = 0; - GPUIntegerCoordinate mipLevelCount; - GPUIntegerCoordinate baseArrayLayer = 0; - GPUIntegerCoordinate arrayLayerCount; -}; - -enum GPUTextureViewDimension { - "1d", - "2d", - "2d-array", - "cube", - "cube-array", - "3d", -}; - -enum GPUTextureAspect { - "all", - "stencil-only", - "depth-only", -}; - -enum GPUTextureFormat { - // 8-bit formats - "r8unorm", - "r8snorm", - "r8uint", - "r8sint", - - // 16-bit formats - "r16uint", - "r16sint", - "r16float", - "rg8unorm", - "rg8snorm", - "rg8uint", - "rg8sint", - - // 32-bit formats - "r32uint", - "r32sint", - "r32float", - "rg16uint", - "rg16sint", - "rg16float", - "rgba8unorm", - "rgba8unorm-srgb", - "rgba8snorm", - "rgba8uint", - "rgba8sint", - "bgra8unorm", - "bgra8unorm-srgb", - // Packed 32-bit formats - "rgb9e5ufloat", - "rgb10a2uint", - "rgb10a2unorm", - "rg11b10ufloat", - - // 64-bit formats - "rg32uint", - "rg32sint", - "rg32float", - "rgba16uint", - "rgba16sint", - "rgba16float", - - // 128-bit formats - "rgba32uint", - "rgba32sint", - "rgba32float", - - // Depth/stencil formats - "stencil8", - "depth16unorm", - "depth24plus", - "depth24plus-stencil8", - "depth32float", - - // "depth32float-stencil8" feature - "depth32float-stencil8", - - // BC compressed formats usable if "texture-compression-bc" is both - // supported by the device/user agent and enabled in requestDevice. - "bc1-rgba-unorm", - "bc1-rgba-unorm-srgb", - "bc2-rgba-unorm", - "bc2-rgba-unorm-srgb", - "bc3-rgba-unorm", - "bc3-rgba-unorm-srgb", - "bc4-r-unorm", - "bc4-r-snorm", - "bc5-rg-unorm", - "bc5-rg-snorm", - "bc6h-rgb-ufloat", - "bc6h-rgb-float", - "bc7-rgba-unorm", - "bc7-rgba-unorm-srgb", - - // ETC2 compressed formats usable if "texture-compression-etc2" is both - // supported by the device/user agent and enabled in requestDevice. - "etc2-rgb8unorm", - "etc2-rgb8unorm-srgb", - "etc2-rgb8a1unorm", - "etc2-rgb8a1unorm-srgb", - "etc2-rgba8unorm", - "etc2-rgba8unorm-srgb", - "eac-r11unorm", - "eac-r11snorm", - "eac-rg11unorm", - "eac-rg11snorm", - - // ASTC compressed formats usable if "texture-compression-astc" is both - // supported by the device/user agent and enabled in requestDevice. - "astc-4x4-unorm", - "astc-4x4-unorm-srgb", - "astc-5x4-unorm", - "astc-5x4-unorm-srgb", - "astc-5x5-unorm", - "astc-5x5-unorm-srgb", - "astc-6x5-unorm", - "astc-6x5-unorm-srgb", - "astc-6x6-unorm", - "astc-6x6-unorm-srgb", - "astc-8x5-unorm", - "astc-8x5-unorm-srgb", - "astc-8x6-unorm", - "astc-8x6-unorm-srgb", - "astc-8x8-unorm", - "astc-8x8-unorm-srgb", - "astc-10x5-unorm", - "astc-10x5-unorm-srgb", - "astc-10x6-unorm", - "astc-10x6-unorm-srgb", - "astc-10x8-unorm", - "astc-10x8-unorm-srgb", - "astc-10x10-unorm", - "astc-10x10-unorm-srgb", - "astc-12x10-unorm", - "astc-12x10-unorm-srgb", - "astc-12x12-unorm", - "astc-12x12-unorm-srgb", -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUSampler { -}; -GPUSampler includes GPUObjectBase; - -dictionary GPUSamplerDescriptor - : GPUObjectDescriptorBase { - GPUAddressMode addressModeU = "clamp-to-edge"; - GPUAddressMode addressModeV = "clamp-to-edge"; - GPUAddressMode addressModeW = "clamp-to-edge"; - GPUFilterMode magFilter = "nearest"; - GPUFilterMode minFilter = "nearest"; - GPUMipmapFilterMode mipmapFilter = "nearest"; - float lodMinClamp = 0; - float lodMaxClamp = 32; - GPUCompareFunction compare; - [Clamp] unsigned short maxAnisotropy = 1; -}; - -enum GPUAddressMode { - "clamp-to-edge", - "repeat", - "mirror-repeat", -}; - -enum GPUFilterMode { - "nearest", - "linear", -}; - -enum GPUMipmapFilterMode { - "nearest", - "linear", -}; - -enum GPUCompareFunction { - "never", - "less", - "equal", - "less-equal", - "greater", - "not-equal", - "greater-equal", - "always", -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUBindGroupLayout { -}; -GPUBindGroupLayout includes GPUObjectBase; - -dictionary GPUBindGroupLayoutDescriptor - : GPUObjectDescriptorBase { - required sequence entries; -}; - -dictionary GPUBindGroupLayoutEntry { - required GPUIndex32 binding; - required GPUShaderStageFlags visibility; - - GPUBufferBindingLayout buffer; - GPUSamplerBindingLayout sampler; - GPUTextureBindingLayout texture; - GPUStorageTextureBindingLayout storageTexture; -}; - -typedef [EnforceRange] unsigned long GPUShaderStageFlags; -[Exposed=(Window, Worker), SecureContext] -namespace GPUShaderStage { - const GPUFlagsConstant VERTEX = 0x1; - const GPUFlagsConstant FRAGMENT = 0x2; - const GPUFlagsConstant COMPUTE = 0x4; -}; - -enum GPUBufferBindingType { - "uniform", - "storage", - "read-only-storage", -}; - -dictionary GPUBufferBindingLayout { - GPUBufferBindingType type = "uniform"; - boolean hasDynamicOffset = false; - GPUSize64 minBindingSize = 0; -}; - -enum GPUSamplerBindingType { - "filtering", - "non-filtering", - "comparison", -}; - -dictionary GPUSamplerBindingLayout { - GPUSamplerBindingType type = "filtering"; -}; - -enum GPUTextureSampleType { - "float", - "unfilterable-float", - "depth", - "sint", - "uint", -}; - -dictionary GPUTextureBindingLayout { - GPUTextureSampleType sampleType = "float"; - GPUTextureViewDimension viewDimension = "2d"; - boolean multisampled = false; -}; - -enum GPUStorageTextureAccess { - "write-only", - "read-only", - "read-write", -}; - -dictionary GPUStorageTextureBindingLayout { - GPUStorageTextureAccess access = "write-only"; - required GPUTextureFormat format; - GPUTextureViewDimension viewDimension = "2d"; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUBindGroup { -}; -GPUBindGroup includes GPUObjectBase; - -dictionary GPUBindGroupDescriptor - : GPUObjectDescriptorBase { - required GPUBindGroupLayout layout; - required sequence entries; -}; - -typedef (GPUSampler or GPUTextureView or GPUBufferBinding) GPUBindingResource; - -dictionary GPUBindGroupEntry { - required GPUIndex32 binding; - required GPUBindingResource resource; -}; - -dictionary GPUBufferBinding { - required GPUBuffer buffer; - GPUSize64 offset = 0; - GPUSize64 size; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUPipelineLayout { -}; -GPUPipelineLayout includes GPUObjectBase; - -dictionary GPUPipelineLayoutDescriptor - : GPUObjectDescriptorBase { - required sequence bindGroupLayouts; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUShaderModule { -}; -GPUShaderModule includes GPUObjectBase; - -dictionary GPUShaderModuleDescriptor - : GPUObjectDescriptorBase { - required USVString code; -}; - -enum GPUCompilationMessageType { - "error", - "warning", - "info", -}; - -[Exposed=(Window, Worker), Serializable, SecureContext] -interface GPUCompilationMessage { - readonly attribute DOMString message; - readonly attribute GPUCompilationMessageType type; - readonly attribute unsigned long long lineNum; - readonly attribute unsigned long long linePos; - readonly attribute unsigned long long offset; - readonly attribute unsigned long long length; -}; - -[Exposed=(Window, Worker), Serializable, SecureContext] -interface GPUCompilationInfo { - readonly attribute FrozenArray messages; -}; - -[Exposed=(Window, Worker), SecureContext, Serializable] -interface GPUPipelineError : DOMException { - constructor(optional DOMString message = "", GPUPipelineErrorInit options); - readonly attribute GPUPipelineErrorReason reason; -}; - -dictionary GPUPipelineErrorInit { - required GPUPipelineErrorReason reason; -}; - -enum GPUPipelineErrorReason { - "validation", - "internal", -}; - -enum GPUAutoLayoutMode { - "auto", -}; - -dictionary GPUPipelineDescriptorBase - : GPUObjectDescriptorBase { - required (GPUPipelineLayout or GPUAutoLayoutMode) layout; -}; - -interface mixin GPUPipelineBase { - [NewObject] GPUBindGroupLayout getBindGroupLayout(unsigned long index); -}; - -dictionary GPUProgrammableStage { - required GPUShaderModule module; - USVString entryPoint; - record constants = {}; -}; - -typedef double GPUPipelineConstantValue; // May represent WGSL's bool, f32, i32, u32, and f16 if enabled. - -[Exposed=(Window, Worker), SecureContext] -interface GPUComputePipeline { -}; -GPUComputePipeline includes GPUObjectBase; -GPUComputePipeline includes GPUPipelineBase; - -dictionary GPUComputePipelineDescriptor - : GPUPipelineDescriptorBase { - required GPUProgrammableStage compute; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPURenderPipeline { -}; -GPURenderPipeline includes GPUObjectBase; -GPURenderPipeline includes GPUPipelineBase; - -dictionary GPURenderPipelineDescriptor - : GPUPipelineDescriptorBase { - required GPUVertexState vertex; - GPUPrimitiveState primitive = {}; - GPUDepthStencilState depthStencil; - GPUMultisampleState multisample = {}; - GPUFragmentState fragment; -}; - -dictionary GPUPrimitiveState { - GPUPrimitiveTopology topology = "triangle-list"; - GPUIndexFormat stripIndexFormat; - GPUFrontFace frontFace = "ccw"; - GPUCullMode cullMode = "none"; - - // Requires "depth-clip-control" feature. - boolean unclippedDepth = false; -}; - -enum GPUPrimitiveTopology { - "point-list", - "line-list", - "line-strip", - "triangle-list", - "triangle-strip", -}; - -enum GPUFrontFace { - "ccw", - "cw", -}; - -enum GPUCullMode { - "none", - "front", - "back", -}; - -dictionary GPUMultisampleState { - GPUSize32 count = 1; - GPUSampleMask mask = 0xFFFFFFFF; - boolean alphaToCoverageEnabled = false; -}; - -dictionary GPUFragmentState - : GPUProgrammableStage { - required sequence targets; -}; - -dictionary GPUColorTargetState { - required GPUTextureFormat format; - - GPUBlendState blend; - GPUColorWriteFlags writeMask = 0xF; // GPUColorWrite.ALL -}; - -dictionary GPUBlendState { - required GPUBlendComponent color; - required GPUBlendComponent alpha; -}; - -typedef [EnforceRange] unsigned long GPUColorWriteFlags; -[Exposed=(Window, Worker), SecureContext] -namespace GPUColorWrite { - const GPUFlagsConstant RED = 0x1; - const GPUFlagsConstant GREEN = 0x2; - const GPUFlagsConstant BLUE = 0x4; - const GPUFlagsConstant ALPHA = 0x8; - const GPUFlagsConstant ALL = 0xF; -}; - -dictionary GPUBlendComponent { - GPUBlendOperation operation = "add"; - GPUBlendFactor srcFactor = "one"; - GPUBlendFactor dstFactor = "zero"; -}; - -enum GPUBlendFactor { - "zero", - "one", - "src", - "one-minus-src", - "src-alpha", - "one-minus-src-alpha", - "dst", - "one-minus-dst", - "dst-alpha", - "one-minus-dst-alpha", - "src-alpha-saturated", - "constant", - "one-minus-constant", - "src1", - "one-minus-src1", - "src1-alpha", - "one-minus-src1-alpha", -}; - -enum GPUBlendOperation { - "add", - "subtract", - "reverse-subtract", - "min", - "max", -}; - -dictionary GPUDepthStencilState { - required GPUTextureFormat format; - - boolean depthWriteEnabled; - GPUCompareFunction depthCompare; - - GPUStencilFaceState stencilFront = {}; - GPUStencilFaceState stencilBack = {}; - - GPUStencilValue stencilReadMask = 0xFFFFFFFF; - GPUStencilValue stencilWriteMask = 0xFFFFFFFF; - - GPUDepthBias depthBias = 0; - float depthBiasSlopeScale = 0; - float depthBiasClamp = 0; -}; - -dictionary GPUStencilFaceState { - GPUCompareFunction compare = "always"; - GPUStencilOperation failOp = "keep"; - GPUStencilOperation depthFailOp = "keep"; - GPUStencilOperation passOp = "keep"; -}; - -enum GPUStencilOperation { - "keep", - "zero", - "replace", - "invert", - "increment-clamp", - "decrement-clamp", - "increment-wrap", - "decrement-wrap", -}; - -enum GPUIndexFormat { - "uint16", - "uint32", -}; - -enum GPUVertexFormat { - "uint8x2", - "uint8x4", - "sint8x2", - "sint8x4", - "unorm8x2", - "unorm8x4", - "snorm8x2", - "snorm8x4", - "uint16x2", - "uint16x4", - "sint16x2", - "sint16x4", - "unorm16x2", - "unorm16x4", - "snorm16x2", - "snorm16x4", - "float16x2", - "float16x4", - "float32", - "float32x2", - "float32x3", - "float32x4", - "uint32", - "uint32x2", - "uint32x3", - "uint32x4", - "sint32", - "sint32x2", - "sint32x3", - "sint32x4", - "unorm10-10-10-2", -}; - -enum GPUVertexStepMode { - "vertex", - "instance", -}; - -dictionary GPUVertexState - : GPUProgrammableStage { - sequence buffers = []; -}; - -dictionary GPUVertexBufferLayout { - required GPUSize64 arrayStride; - GPUVertexStepMode stepMode = "vertex"; - required sequence attributes; -}; - -dictionary GPUVertexAttribute { - required GPUVertexFormat format; - required GPUSize64 offset; - - required GPUIndex32 shaderLocation; -}; - -dictionary GPUTexelCopyBufferLayout { - GPUSize64 offset = 0; - GPUSize32 bytesPerRow; - GPUSize32 rowsPerImage; -}; - -dictionary GPUTexelCopyBufferInfo - : GPUTexelCopyBufferLayout { - required GPUBuffer buffer; -}; - -dictionary GPUTexelCopyTextureInfo { - required GPUTexture texture; - GPUIntegerCoordinate mipLevel = 0; - GPUOrigin3D origin = {}; - GPUTextureAspect aspect = "all"; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUCommandBuffer { -}; -GPUCommandBuffer includes GPUObjectBase; - -dictionary GPUCommandBufferDescriptor - : GPUObjectDescriptorBase { -}; - -interface mixin GPUCommandsMixin { -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUCommandEncoder { - GPURenderPassEncoder beginRenderPass(GPURenderPassDescriptor descriptor); - GPUComputePassEncoder beginComputePass(optional GPUComputePassDescriptor descriptor = {}); - - undefined copyBufferToBuffer( - GPUBuffer source, - GPUSize64 sourceOffset, - GPUBuffer destination, - GPUSize64 destinationOffset, - GPUSize64 size); - - undefined copyBufferToTexture( - GPUTexelCopyBufferInfo source, - GPUTexelCopyTextureInfo destination, - GPUExtent3D copySize); - - undefined copyTextureToBuffer( - GPUTexelCopyTextureInfo source, - GPUTexelCopyBufferInfo destination, - GPUExtent3D copySize); - - undefined copyTextureToTexture( - GPUTexelCopyTextureInfo source, - GPUTexelCopyTextureInfo destination, - GPUExtent3D copySize); - - undefined clearBuffer( - GPUBuffer buffer, - optional GPUSize64 offset = 0, - optional GPUSize64 size); - - undefined resolveQuerySet( - GPUQuerySet querySet, - GPUSize32 firstQuery, - GPUSize32 queryCount, - GPUBuffer destination, - GPUSize64 destinationOffset); - - GPUCommandBuffer finish(optional GPUCommandBufferDescriptor descriptor = {}); -}; -GPUCommandEncoder includes GPUObjectBase; -GPUCommandEncoder includes GPUCommandsMixin; -GPUCommandEncoder includes GPUDebugCommandsMixin; - -dictionary GPUCommandEncoderDescriptor - : GPUObjectDescriptorBase { -}; - -interface mixin GPUBindingCommandsMixin { - undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup, - optional sequence dynamicOffsets = []); - - undefined setBindGroup(GPUIndex32 index, GPUBindGroup? bindGroup, - Uint32Array dynamicOffsetsData, - GPUSize64 dynamicOffsetsDataStart, - GPUSize32 dynamicOffsetsDataLength); -}; - -interface mixin GPUDebugCommandsMixin { - undefined pushDebugGroup(USVString groupLabel); - undefined popDebugGroup(); - undefined insertDebugMarker(USVString markerLabel); -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUComputePassEncoder { - undefined setPipeline(GPUComputePipeline pipeline); - undefined dispatchWorkgroups(GPUSize32 workgroupCountX, optional GPUSize32 workgroupCountY = 1, optional GPUSize32 workgroupCountZ = 1); - undefined dispatchWorkgroupsIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); - - undefined end(); -}; -GPUComputePassEncoder includes GPUObjectBase; -GPUComputePassEncoder includes GPUCommandsMixin; -GPUComputePassEncoder includes GPUDebugCommandsMixin; -GPUComputePassEncoder includes GPUBindingCommandsMixin; - -dictionary GPUComputePassTimestampWrites { - required GPUQuerySet querySet; - GPUSize32 beginningOfPassWriteIndex; - GPUSize32 endOfPassWriteIndex; -}; - -dictionary GPUComputePassDescriptor - : GPUObjectDescriptorBase { - GPUComputePassTimestampWrites timestampWrites; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPURenderPassEncoder { - undefined setViewport(float x, float y, - float width, float height, - float minDepth, float maxDepth); - - undefined setScissorRect(GPUIntegerCoordinate x, GPUIntegerCoordinate y, - GPUIntegerCoordinate width, GPUIntegerCoordinate height); - - undefined setBlendConstant(GPUColor color); - undefined setStencilReference(GPUStencilValue reference); - - undefined beginOcclusionQuery(GPUSize32 queryIndex); - undefined endOcclusionQuery(); - - undefined executeBundles(sequence bundles); - undefined end(); -}; -GPURenderPassEncoder includes GPUObjectBase; -GPURenderPassEncoder includes GPUCommandsMixin; -GPURenderPassEncoder includes GPUDebugCommandsMixin; -GPURenderPassEncoder includes GPUBindingCommandsMixin; -GPURenderPassEncoder includes GPURenderCommandsMixin; - -dictionary GPURenderPassTimestampWrites { - required GPUQuerySet querySet; - GPUSize32 beginningOfPassWriteIndex; - GPUSize32 endOfPassWriteIndex; -}; - -dictionary GPURenderPassDescriptor - : GPUObjectDescriptorBase { - required sequence colorAttachments; - GPURenderPassDepthStencilAttachment depthStencilAttachment; - GPUQuerySet occlusionQuerySet; - GPURenderPassTimestampWrites timestampWrites; -}; - -dictionary GPURenderPassColorAttachment { - required GPUTextureView view; - GPUTextureView resolveTarget; - - GPUColor clearValue; - required GPULoadOp loadOp; - required GPUStoreOp storeOp; -}; - -dictionary GPURenderPassDepthStencilAttachment { - required GPUTextureView view; - - float depthClearValue; - GPULoadOp depthLoadOp; - GPUStoreOp depthStoreOp; - boolean depthReadOnly = false; - - GPUStencilValue stencilClearValue = 0; - GPULoadOp stencilLoadOp; - GPUStoreOp stencilStoreOp; - boolean stencilReadOnly = false; -}; - -enum GPULoadOp { - "load", - "clear", -}; - -enum GPUStoreOp { - "store", - "discard", -}; - -dictionary GPURenderPassLayout - : GPUObjectDescriptorBase { - required sequence colorFormats; - GPUTextureFormat depthStencilFormat; - GPUSize32 sampleCount = 1; -}; - -interface mixin GPURenderCommandsMixin { - undefined setPipeline(GPURenderPipeline pipeline); - - undefined setIndexBuffer(GPUBuffer buffer, GPUIndexFormat indexFormat, optional GPUSize64 offset = 0, optional GPUSize64 size); - undefined setVertexBuffer(GPUIndex32 slot, GPUBuffer buffer, optional GPUSize64 offset = 0, optional GPUSize64 size); - - undefined draw(GPUSize32 vertexCount, optional GPUSize32 instanceCount = 1, - optional GPUSize32 firstVertex = 0, optional GPUSize32 firstInstance = 0); - undefined drawIndexed(GPUSize32 indexCount, optional GPUSize32 instanceCount = 1, - optional GPUSize32 firstIndex = 0, - optional GPUSignedOffset32 baseVertex = 0, - optional GPUSize32 firstInstance = 0); - - undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); - undefined drawIndexedIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset); -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPURenderBundle { -}; -GPURenderBundle includes GPUObjectBase; - -dictionary GPURenderBundleDescriptor - : GPUObjectDescriptorBase { -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPURenderBundleEncoder { - GPURenderBundle finish(optional GPURenderBundleDescriptor descriptor = {}); -}; -GPURenderBundleEncoder includes GPUObjectBase; -GPURenderBundleEncoder includes GPUCommandsMixin; -GPURenderBundleEncoder includes GPUDebugCommandsMixin; -GPURenderBundleEncoder includes GPUBindingCommandsMixin; -GPURenderBundleEncoder includes GPURenderCommandsMixin; - -dictionary GPURenderBundleEncoderDescriptor - : GPURenderPassLayout { - boolean depthReadOnly = false; - boolean stencilReadOnly = false; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUQueue { - undefined submit(sequence commandBuffers); - - Promise onSubmittedWorkDone(); - - undefined writeBuffer( - GPUBuffer buffer, - GPUSize64 bufferOffset, - AllowSharedBufferSource data, - optional GPUSize64 dataOffset = 0, - optional GPUSize64 size); - - undefined writeTexture( - GPUTexelCopyTextureInfo destination, - AllowSharedBufferSource data, - GPUTexelCopyBufferLayout dataLayout, - GPUExtent3D size); -}; -GPUQueue includes GPUObjectBase; - -[Exposed=(Window, Worker), SecureContext] -interface GPUQuerySet { - undefined destroy(); - - readonly attribute GPUQueryType type; - readonly attribute GPUSize32Out count; -}; -GPUQuerySet includes GPUObjectBase; - -dictionary GPUQuerySetDescriptor - : GPUObjectDescriptorBase { - required GPUQueryType type; - required GPUSize32 count; -}; - -enum GPUQueryType { - "occlusion", - "timestamp", -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUCanvasContext { - readonly attribute (HTMLCanvasElement or OffscreenCanvas) canvas; - - undefined configure(GPUCanvasConfiguration configuration); - undefined unconfigure(); - - GPUTexture getCurrentTexture(); -}; - -enum GPUCanvasAlphaMode { - "opaque", - "premultiplied", -}; - -dictionary GPUCanvasConfiguration { - required GPUDevice device; - required GPUTextureFormat format; - GPUTextureUsageFlags usage = 0x10; // GPUTextureUsage.RENDER_ATTACHMENT - sequence viewFormats = []; - GPUCanvasAlphaMode alphaMode = "opaque"; -}; - -enum GPUDeviceLostReason { - "unknown", - "destroyed", -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUDeviceLostInfo { - readonly attribute GPUDeviceLostReason reason; - readonly attribute DOMString message; -}; - -partial interface GPUDevice { - readonly attribute Promise lost; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUError { - readonly attribute DOMString message; -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUValidationError - : GPUError { - constructor(DOMString message); -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUOutOfMemoryError - : GPUError { - constructor(DOMString message); -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUInternalError - : GPUError { - constructor(DOMString message); -}; - -enum GPUErrorFilter { - "validation", - "out-of-memory", - "internal", -}; - -partial interface GPUDevice { - undefined pushErrorScope(GPUErrorFilter filter); - Promise popErrorScope(); -}; - -[Exposed=(Window, Worker), SecureContext] -interface GPUUncapturedErrorEvent : Event { - constructor( - DOMString type, - GPUUncapturedErrorEventInit gpuUncapturedErrorEventInitDict - ); - [SameObject] readonly attribute GPUError error; -}; - -dictionary GPUUncapturedErrorEventInit : EventInit { - required GPUError error; -}; - -partial interface GPUDevice { - attribute EventHandler onuncapturederror; -}; - -typedef [EnforceRange] unsigned long GPUBufferDynamicOffset; -typedef [EnforceRange] unsigned long GPUStencilValue; -typedef [EnforceRange] unsigned long GPUSampleMask; -typedef [EnforceRange] long GPUDepthBias; - -typedef [EnforceRange] unsigned long long GPUSize64; -typedef [EnforceRange] unsigned long GPUIntegerCoordinate; -typedef [EnforceRange] unsigned long GPUIndex32; -typedef [EnforceRange] unsigned long GPUSize32; -typedef [EnforceRange] long GPUSignedOffset32; - -typedef unsigned long long GPUSize64Out; -typedef unsigned long GPUIntegerCoordinateOut; -typedef unsigned long GPUSize32Out; - -typedef unsigned long GPUFlagsConstant; - -dictionary GPUColorDict { - required double r; - required double g; - required double b; - required double a; -}; -typedef (sequence or GPUColorDict) GPUColor; - -dictionary GPUOrigin2DDict { - GPUIntegerCoordinate x = 0; - GPUIntegerCoordinate y = 0; -}; -typedef (sequence or GPUOrigin2DDict) GPUOrigin2D; - -dictionary GPUOrigin3DDict { - GPUIntegerCoordinate x = 0; - GPUIntegerCoordinate y = 0; - GPUIntegerCoordinate z = 0; -}; -typedef (sequence or GPUOrigin3DDict) GPUOrigin3D; - -dictionary GPUExtent3DDict { - required GPUIntegerCoordinate width; - GPUIntegerCoordinate height = 1; - GPUIntegerCoordinate depthOrArrayLayers = 1; -}; -typedef (sequence or GPUExtent3DDict) GPUExtent3D; diff --git a/deno_webgpu/webidl.rs b/deno_webgpu/webidl.rs index 533da00c830..0322dcfe419 100644 --- a/deno_webgpu/webidl.rs +++ b/deno_webgpu/webidl.rs @@ -17,432 +17,459 @@ use deno_error::JsErrorBox; #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUExtent3DDict { - #[options(enforce_range = true)] - width: u32, - #[webidl(default = 1)] - #[options(enforce_range = true)] - height: u32, - #[webidl(default = 1)] - #[options(enforce_range = true)] - depth_or_array_layers: u32, + #[options(enforce_range = true)] + width: u32, + #[webidl(default = 1)] + #[options(enforce_range = true)] + height: u32, + #[webidl(default = 1)] + #[options(enforce_range = true)] + depth_or_array_layers: u32, } pub(crate) enum GPUExtent3D { - Dict(GPUExtent3DDict), - Sequence((u32, u32, u32)), + Dict(GPUExtent3DDict), + Sequence((u32, u32, u32)), } impl<'a> WebIdlConverter<'a> for GPUExtent3D { - type Options = (); - - fn convert<'b>( - scope: &mut v8::HandleScope<'a>, - value: v8::Local<'a, v8::Value>, - prefix: Cow<'static, str>, - context: ContextFn<'b>, - options: &Self::Options, - ) -> Result { - if value.is_null_or_undefined() { - return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert( - scope, - value, - prefix, - context.borrowed(), - options, - )?)); - } - if let Ok(obj) = value.try_cast::() { - let iter = v8::Symbol::get_iterator(scope); - if let Some(iter) = obj.get(scope, iter.into()) { - if !iter.is_undefined() { - let conv = >::convert( - scope, - value, - prefix.clone(), - context.borrowed(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )?; - if conv.is_empty() || conv.len() > 3 { - return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUExtent3D must have between 1 and 3 elements, received {} elements", conv.len())))); - } - - let mut iter = conv.into_iter(); - return Ok(GPUExtent3D::Sequence(( - iter.next().unwrap(), - iter.next().unwrap_or(1), - iter.next().unwrap_or(1), - ))); - } - } - - return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert( - scope, value, prefix, context, options, - )?)); + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_null_or_undefined() { + return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert( + scope, + value, + prefix, + context.borrowed(), + options, + )?)); + } + if let Ok(obj) = value.try_cast::() { + let iter = v8::Symbol::get_iterator(scope); + if let Some(iter) = obj.get(scope, iter.into()) { + if !iter.is_undefined() { + let conv = >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )?; + if conv.is_empty() || conv.len() > 3 { + return Err(WebIdlError::other( + prefix, + context, + JsErrorBox::type_error(format!( + "A sequence of number used as a GPUExtent3D must have between 1 and 3 elements, received {} elements", + conv.len() + )), + )); + } + + let mut iter = conv.into_iter(); + return Ok(GPUExtent3D::Sequence(( + iter.next().unwrap(), + iter.next().unwrap_or(1), + iter.next().unwrap_or(1), + ))); } + } - Err(WebIdlError::new( - prefix, - context, - WebIdlErrorKind::ConvertToConverterType( - "sequence or GPUExtent3DDict", - ), - )) + return Ok(GPUExtent3D::Dict(GPUExtent3DDict::convert( + scope, value, prefix, context, options, + )?)); } + + Err(WebIdlError::new( + prefix, + context, + WebIdlErrorKind::ConvertToConverterType( + "sequence or GPUExtent3DDict", + ), + )) + } } impl From for wgpu_types::Extent3d { - fn from(value: GPUExtent3D) -> Self { - match value { - GPUExtent3D::Dict(dict) => Self { - width: dict.width, - height: dict.height, - depth_or_array_layers: dict.depth_or_array_layers, - }, - GPUExtent3D::Sequence((width, height, depth)) => Self { - width, - height, - depth_or_array_layers: depth, - }, - } - } + fn from(value: GPUExtent3D) -> Self { + match value { + GPUExtent3D::Dict(dict) => Self { + width: dict.width, + height: dict.height, + depth_or_array_layers: dict.depth_or_array_layers, + }, + GPUExtent3D::Sequence((width, height, depth)) => Self { + width, + height, + depth_or_array_layers: depth, + }, + } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUOrigin3DDict { - #[webidl(default = 0)] - #[options(enforce_range = true)] - x: u32, - #[webidl(default = 0)] - #[options(enforce_range = true)] - y: u32, - #[webidl(default = 0)] - #[options(enforce_range = true)] - z: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + x: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + y: u32, + #[webidl(default = 0)] + #[options(enforce_range = true)] + z: u32, } pub(crate) enum GPUOrigin3D { - Dict(GPUOrigin3DDict), - Sequence((u32, u32, u32)), + Dict(GPUOrigin3DDict), + Sequence((u32, u32, u32)), } impl Default for GPUOrigin3D { - fn default() -> Self { - GPUOrigin3D::Sequence((0, 0, 0)) - } + fn default() -> Self { + GPUOrigin3D::Sequence((0, 0, 0)) + } } impl<'a> WebIdlConverter<'a> for GPUOrigin3D { - type Options = (); - - fn convert<'b>( - scope: &mut v8::HandleScope<'a>, - value: v8::Local<'a, v8::Value>, - prefix: Cow<'static, str>, - context: ContextFn<'b>, - options: &Self::Options, - ) -> Result { - if value.is_null_or_undefined() { - return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert( - scope, - value, - prefix, - context.borrowed(), - options, - )?)); - } - if let Ok(obj) = value.try_cast::() { - let iter = v8::Symbol::get_iterator(scope); - if let Some(iter) = obj.get(scope, iter.into()) { - if !iter.is_undefined() { - let conv = >::convert( - scope, - value, - prefix.clone(), - context.borrowed(), - &IntOptions { - clamp: false, - enforce_range: true, - }, - )?; - if conv.len() > 3 { - return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUOrigin3D must have at most 3 elements, received {} elements", conv.len())))); - } - - let mut iter = conv.into_iter(); - return Ok(GPUOrigin3D::Sequence(( - iter.next().unwrap_or(0), - iter.next().unwrap_or(0), - iter.next().unwrap_or(0), - ))); - } - } - - return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert( - scope, value, prefix, context, options, - )?)); + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_null_or_undefined() { + return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert( + scope, + value, + prefix, + context.borrowed(), + options, + )?)); + } + if let Ok(obj) = value.try_cast::() { + let iter = v8::Symbol::get_iterator(scope); + if let Some(iter) = obj.get(scope, iter.into()) { + if !iter.is_undefined() { + let conv = >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + &IntOptions { + clamp: false, + enforce_range: true, + }, + )?; + if conv.len() > 3 { + return Err(WebIdlError::other( + prefix, + context, + JsErrorBox::type_error(format!( + "A sequence of number used as a GPUOrigin3D must have at most 3 elements, received {} elements", + conv.len() + )), + )); + } + + let mut iter = conv.into_iter(); + return Ok(GPUOrigin3D::Sequence(( + iter.next().unwrap_or(0), + iter.next().unwrap_or(0), + iter.next().unwrap_or(0), + ))); } + } - Err(WebIdlError::new( - prefix, - context, - WebIdlErrorKind::ConvertToConverterType( - "sequence or GPUOrigin3DDict", - ), - )) + return Ok(GPUOrigin3D::Dict(GPUOrigin3DDict::convert( + scope, value, prefix, context, options, + )?)); } + + Err(WebIdlError::new( + prefix, + context, + WebIdlErrorKind::ConvertToConverterType( + "sequence or GPUOrigin3DDict", + ), + )) + } } impl From for wgpu_types::Origin3d { - fn from(value: GPUOrigin3D) -> Self { - match value { - GPUOrigin3D::Dict(dict) => Self { - x: dict.x, - y: dict.y, - z: dict.z, - }, - GPUOrigin3D::Sequence((x, y, z)) => Self { x, y, z }, - } - } + fn from(value: GPUOrigin3D) -> Self { + match value { + GPUOrigin3D::Dict(dict) => Self { + x: dict.x, + y: dict.y, + z: dict.z, + }, + GPUOrigin3D::Sequence((x, y, z)) => Self { x, y, z }, + } + } } #[derive(WebIDL)] #[webidl(dictionary)] pub(crate) struct GPUColorDict { - r: f64, - g: f64, - b: f64, - a: f64, + r: f64, + g: f64, + b: f64, + a: f64, } pub(crate) enum GPUColor { - Dict(GPUColorDict), - Sequence((f64, f64, f64, f64)), + Dict(GPUColorDict), + Sequence((f64, f64, f64, f64)), } impl<'a> WebIdlConverter<'a> for GPUColor { - type Options = (); - - fn convert<'b>( - scope: &mut v8::HandleScope<'a>, - value: v8::Local<'a, v8::Value>, - prefix: Cow<'static, str>, - context: ContextFn<'b>, - options: &Self::Options, - ) -> Result { - if value.is_null_or_undefined() { - return Ok(GPUColor::Dict(GPUColorDict::convert( - scope, - value, - prefix, - context.borrowed(), - options, - )?)); - } - if let Ok(obj) = value.try_cast::() { - let iter = v8::Symbol::get_iterator(scope); - if let Some(iter) = obj.get(scope, iter.into()) { - if !iter.is_undefined() { - let conv = >::convert( - scope, - value, - prefix.clone(), - context.borrowed(), - options, - )?; - if conv.len() != 4 { - return Err(WebIdlError::other(prefix, context, JsErrorBox::type_error(format!("A sequence of number used as a GPUColor must have exactly 4 elements, received {} elements", conv.len())))); - } - - let mut iter = conv.into_iter(); - return Ok(GPUColor::Sequence(( - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - iter.next().unwrap(), - ))); - } - } - - return Ok(GPUColor::Dict(GPUColorDict::convert( - scope, value, prefix, context, options, - )?)); + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_null_or_undefined() { + return Ok(GPUColor::Dict(GPUColorDict::convert( + scope, + value, + prefix, + context.borrowed(), + options, + )?)); + } + if let Ok(obj) = value.try_cast::() { + let iter = v8::Symbol::get_iterator(scope); + if let Some(iter) = obj.get(scope, iter.into()) { + if !iter.is_undefined() { + let conv = >::convert( + scope, + value, + prefix.clone(), + context.borrowed(), + options, + )?; + if conv.len() != 4 { + return Err(WebIdlError::other( + prefix, + context, + JsErrorBox::type_error(format!( + "A sequence of number used as a GPUColor must have exactly 4 elements, received {} elements", + conv.len() + )), + )); + } + + let mut iter = conv.into_iter(); + return Ok(GPUColor::Sequence(( + iter.next().unwrap(), + iter.next().unwrap(), + iter.next().unwrap(), + iter.next().unwrap(), + ))); } + } - Err(WebIdlError::new( - prefix, - context, - WebIdlErrorKind::ConvertToConverterType( - "sequence or GPUOrigin3DDict", - ), - )) + return Ok(GPUColor::Dict(GPUColorDict::convert( + scope, value, prefix, context, options, + )?)); } + + Err(WebIdlError::new( + prefix, + context, + WebIdlErrorKind::ConvertToConverterType( + "sequence or GPUOrigin3DDict", + ), + )) + } } impl From for wgpu_types::Color { - fn from(value: GPUColor) -> Self { - match value { - GPUColor::Dict(dict) => Self { - r: dict.r, - g: dict.g, - b: dict.b, - a: dict.a, - }, - GPUColor::Sequence((r, g, b, a)) => Self { r, g, b, a }, - } - } + fn from(value: GPUColor) -> Self { + match value { + GPUColor::Dict(dict) => Self { + r: dict.r, + g: dict.g, + b: dict.b, + a: dict.a, + }, + GPUColor::Sequence((r, g, b, a)) => Self { r, g, b, a }, + } + } } #[derive(WebIDL)] #[webidl(enum)] pub(crate) enum GPUAutoLayoutMode { - Auto, + Auto, } pub(crate) enum GPUPipelineLayoutOrGPUAutoLayoutMode { - PipelineLayout(Ptr), - AutoLayoutMode(GPUAutoLayoutMode), + PipelineLayout(Ptr), + AutoLayoutMode(GPUAutoLayoutMode), } -impl From for Option { - fn from(value: GPUPipelineLayoutOrGPUAutoLayoutMode) -> Self { - match value { - GPUPipelineLayoutOrGPUAutoLayoutMode::PipelineLayout(layout) => Some(layout.id), - GPUPipelineLayoutOrGPUAutoLayoutMode::AutoLayoutMode(GPUAutoLayoutMode::Auto) => None, - } - } +impl From + for Option +{ + fn from(value: GPUPipelineLayoutOrGPUAutoLayoutMode) -> Self { + match value { + GPUPipelineLayoutOrGPUAutoLayoutMode::PipelineLayout(layout) => { + Some(layout.id) + } + GPUPipelineLayoutOrGPUAutoLayoutMode::AutoLayoutMode( + GPUAutoLayoutMode::Auto, + ) => None, + } + } } impl<'a> WebIdlConverter<'a> for GPUPipelineLayoutOrGPUAutoLayoutMode { - type Options = (); - - fn convert<'b>( - scope: &mut v8::HandleScope<'a>, - value: v8::Local<'a, v8::Value>, - prefix: Cow<'static, str>, - context: ContextFn<'b>, - options: &Self::Options, - ) -> Result { - if value.is_object() { - Ok(Self::PipelineLayout(WebIdlConverter::convert( - scope, value, prefix, context, options, - )?)) - } else { - Ok(Self::AutoLayoutMode(WebIdlConverter::convert( - scope, value, prefix, context, options, - )?)) - } - } + type Options = (); + + fn convert<'b>( + scope: &mut v8::HandleScope<'a>, + value: v8::Local<'a, v8::Value>, + prefix: Cow<'static, str>, + context: ContextFn<'b>, + options: &Self::Options, + ) -> Result { + if value.is_object() { + Ok(Self::PipelineLayout(WebIdlConverter::convert( + scope, value, prefix, context, options, + )?)) + } else { + Ok(Self::AutoLayoutMode(WebIdlConverter::convert( + scope, value, prefix, context, options, + )?)) + } + } } #[derive(WebIDL, Clone, Hash, Eq, PartialEq)] #[webidl(enum)] pub enum GPUFeatureName { - #[webidl(rename = "depth-clip-control")] - DepthClipControl, - #[webidl(rename = "timestamp-query")] - TimestampQuery, - #[webidl(rename = "indirect-first-instance")] - IndirectFirstInstance, - #[webidl(rename = "shader-f16")] - ShaderF16, - #[webidl(rename = "depth32float-stencil8")] - Depth32floatStencil8, - #[webidl(rename = "texture-compression-bc")] - TextureCompressionBc, - #[webidl(rename = "texture-compression-bc-sliced-3d")] - TextureCompressionBcSliced3d, - #[webidl(rename = "texture-compression-etc2")] - TextureCompressionEtc2, - #[webidl(rename = "texture-compression-astc")] - TextureCompressionAstc, - #[webidl(rename = "texture-compression-astc-sliced-3d")] - TextureCompressionAstcSliced3d, - #[webidl(rename = "rg11b10ufloat-renderable")] - Rg11b10ufloatRenderable, - #[webidl(rename = "bgra8unorm-storage")] - Bgra8unormStorage, - #[webidl(rename = "float32-filterable")] - Float32Filterable, - #[webidl(rename = "dual-source-blending")] - DualSourceBlending, - #[webidl(rename = "subgroups")] - Subgroups, - - // extended from spec - #[webidl(rename = "texture-format-16-bit-norm")] - TextureFormat16BitNorm, - #[webidl(rename = "texture-compression-astc-hdr")] - TextureCompressionAstcHdr, - #[webidl(rename = "texture-adapter-specific-format-features")] - TextureAdapterSpecificFormatFeatures, - #[webidl(rename = "pipeline-statistics-query")] - PipelineStatisticsQuery, - #[webidl(rename = "timestamp-query-inside-passes")] - TimestampQueryInsidePasses, - #[webidl(rename = "mappable-primary-buffers")] - MappablePrimaryBuffers, - #[webidl(rename = "texture-binding-array")] - TextureBindingArray, - #[webidl(rename = "buffer-binding-array")] - BufferBindingArray, - #[webidl(rename = "storage-resource-binding-array")] - StorageResourceBindingArray, - #[webidl(rename = "sampled-texture-and-storage-buffer-array-non-uniform-indexing")] - SampledTextureAndStorageBufferArrayNonUniformIndexing, - #[webidl(rename = "storage-texture-array-non-uniform-indexing")] - StorageTextureArrayNonUniformIndexing, - #[webidl(rename = "uniform-buffer-binding-arrays")] - UniformBufferBindingArrays, - #[webidl(rename = "partially-bound-binding-array")] - PartiallyBoundBindingArray, - #[webidl(rename = "multi-draw-indirect")] - MultiDrawIndirect, - #[webidl(rename = "multi-draw-indirect-count")] - MultiDrawIndirectCount, - #[webidl(rename = "push-constants")] - PushConstants, - #[webidl(rename = "address-mode-clamp-to-zero")] - AddressModeClampToZero, - #[webidl(rename = "address-mode-clamp-to-border")] - AddressModeClampToBorder, - #[webidl(rename = "polygon-mode-line")] - PolygonModeLine, - #[webidl(rename = "polygon-mode-point")] - PolygonModePoint, - #[webidl(rename = "conservative-rasterization")] - ConservativeRasterization, - #[webidl(rename = "vertex-writable-storage")] - VertexWritableStorage, - #[webidl(rename = "clear-texture")] - ClearTexture, - #[webidl(rename = "msl-shader-passthrough")] - MslShaderPassthrough, - #[webidl(rename = "spirv-shader-passthrough")] - SpirvShaderPassthrough, - #[webidl(rename = "multiview")] - Multiview, - #[webidl(rename = "vertex-attribute-64-bit")] - VertexAttribute64Bit, - #[webidl(rename = "shader-f64")] - ShaderF64, - #[webidl(rename = "shader-i16")] - ShaderI16, - #[webidl(rename = "shader-primitive-index")] - ShaderPrimitiveIndex, - #[webidl(rename = "shader-early-depth-test")] - ShaderEarlyDepthTest, + #[webidl(rename = "depth-clip-control")] + DepthClipControl, + #[webidl(rename = "timestamp-query")] + TimestampQuery, + #[webidl(rename = "indirect-first-instance")] + IndirectFirstInstance, + #[webidl(rename = "shader-f16")] + ShaderF16, + #[webidl(rename = "depth32float-stencil8")] + Depth32floatStencil8, + #[webidl(rename = "texture-compression-bc")] + TextureCompressionBc, + #[webidl(rename = "texture-compression-bc-sliced-3d")] + TextureCompressionBcSliced3d, + #[webidl(rename = "texture-compression-etc2")] + TextureCompressionEtc2, + #[webidl(rename = "texture-compression-astc")] + TextureCompressionAstc, + #[webidl(rename = "texture-compression-astc-sliced-3d")] + TextureCompressionAstcSliced3d, + #[webidl(rename = "rg11b10ufloat-renderable")] + Rg11b10ufloatRenderable, + #[webidl(rename = "bgra8unorm-storage")] + Bgra8unormStorage, + #[webidl(rename = "float32-filterable")] + Float32Filterable, + #[webidl(rename = "dual-source-blending")] + DualSourceBlending, + #[webidl(rename = "subgroups")] + Subgroups, + + // extended from spec + #[webidl(rename = "texture-format-16-bit-norm")] + TextureFormat16BitNorm, + #[webidl(rename = "texture-compression-astc-hdr")] + TextureCompressionAstcHdr, + #[webidl(rename = "texture-adapter-specific-format-features")] + TextureAdapterSpecificFormatFeatures, + #[webidl(rename = "pipeline-statistics-query")] + PipelineStatisticsQuery, + #[webidl(rename = "timestamp-query-inside-passes")] + TimestampQueryInsidePasses, + #[webidl(rename = "mappable-primary-buffers")] + MappablePrimaryBuffers, + #[webidl(rename = "texture-binding-array")] + TextureBindingArray, + #[webidl(rename = "buffer-binding-array")] + BufferBindingArray, + #[webidl(rename = "storage-resource-binding-array")] + StorageResourceBindingArray, + #[webidl( + rename = "sampled-texture-and-storage-buffer-array-non-uniform-indexing" + )] + SampledTextureAndStorageBufferArrayNonUniformIndexing, + #[webidl(rename = "storage-texture-array-non-uniform-indexing")] + StorageTextureArrayNonUniformIndexing, + #[webidl(rename = "uniform-buffer-binding-arrays")] + UniformBufferBindingArrays, + #[webidl(rename = "partially-bound-binding-array")] + PartiallyBoundBindingArray, + #[webidl(rename = "multi-draw-indirect-count")] + MultiDrawIndirectCount, + #[webidl(rename = "push-constants")] + PushConstants, + #[webidl(rename = "address-mode-clamp-to-zero")] + AddressModeClampToZero, + #[webidl(rename = "address-mode-clamp-to-border")] + AddressModeClampToBorder, + #[webidl(rename = "polygon-mode-line")] + PolygonModeLine, + #[webidl(rename = "polygon-mode-point")] + PolygonModePoint, + #[webidl(rename = "conservative-rasterization")] + ConservativeRasterization, + #[webidl(rename = "vertex-writable-storage")] + VertexWritableStorage, + #[webidl(rename = "clear-texture")] + ClearTexture, + #[webidl(rename = "multiview")] + Multiview, + #[webidl(rename = "vertex-attribute-64-bit")] + VertexAttribute64Bit, + #[webidl(rename = "shader-f64")] + ShaderF64, + #[webidl(rename = "shader-i16")] + ShaderI16, + #[webidl(rename = "shader-primitive-index")] + ShaderPrimitiveIndex, + #[webidl(rename = "shader-early-depth-test")] + ShaderEarlyDepthTest, + #[webidl(rename = "passthrough-shaders")] + PassthroughShaders, } -pub fn feature_names_to_features(names: Vec) -> wgpu_types::Features { - use wgpu_types::Features; - let mut features = Features::empty(); +pub fn feature_names_to_features( + names: Vec, +) -> wgpu_types::Features { + use wgpu_types::Features; + let mut features = Features::empty(); - for name in names { - #[rustfmt::skip] + for name in names { + #[rustfmt::skip] let feature = match name { GPUFeatureName::DepthClipControl => Features::DEPTH_CLIP_CONTROL, GPUFeatureName::TimestampQuery => Features::TIMESTAMP_QUERY, @@ -472,7 +499,6 @@ pub fn feature_names_to_features(names: Vec) -> wgpu_types::Feat GPUFeatureName::StorageTextureArrayNonUniformIndexing => Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, GPUFeatureName::UniformBufferBindingArrays => Features::UNIFORM_BUFFER_BINDING_ARRAYS, GPUFeatureName::PartiallyBoundBindingArray => Features::PARTIALLY_BOUND_BINDING_ARRAY, - GPUFeatureName::MultiDrawIndirect => Features::MULTI_DRAW_INDIRECT, GPUFeatureName::MultiDrawIndirectCount => Features::MULTI_DRAW_INDIRECT_COUNT, GPUFeatureName::PushConstants => Features::PUSH_CONSTANTS, GPUFeatureName::AddressModeClampToZero => Features::ADDRESS_MODE_CLAMP_TO_ZERO, @@ -482,172 +508,175 @@ pub fn feature_names_to_features(names: Vec) -> wgpu_types::Feat GPUFeatureName::ConservativeRasterization => Features::CONSERVATIVE_RASTERIZATION, GPUFeatureName::VertexWritableStorage => Features::VERTEX_WRITABLE_STORAGE, GPUFeatureName::ClearTexture => Features::CLEAR_TEXTURE, - GPUFeatureName::MslShaderPassthrough => Features::MSL_SHADER_PASSTHROUGH, - GPUFeatureName::SpirvShaderPassthrough => Features::SPIRV_SHADER_PASSTHROUGH, GPUFeatureName::Multiview => Features::MULTIVIEW, GPUFeatureName::VertexAttribute64Bit => Features::VERTEX_ATTRIBUTE_64BIT, GPUFeatureName::ShaderF64 => Features::SHADER_F64, GPUFeatureName::ShaderI16 => Features::SHADER_I16, GPUFeatureName::ShaderPrimitiveIndex => Features::SHADER_PRIMITIVE_INDEX, GPUFeatureName::ShaderEarlyDepthTest => Features::SHADER_EARLY_DEPTH_TEST, + GPUFeatureName::PassthroughShaders => Features::EXPERIMENTAL_PASSTHROUGH_SHADERS, }; - features.set(feature, true); - } + features.set(feature, true); + } - features + features } #[allow(clippy::disallowed_types)] -pub fn features_to_feature_names(features: wgpu_types::Features) -> HashSet { - use GPUFeatureName::*; - let mut return_features = HashSet::new(); - - // api - if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) { - return_features.insert(DepthClipControl); - } - if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) { - return_features.insert(TimestampQuery); - } - if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) { - return_features.insert(IndirectFirstInstance); - } - // shader - if features.contains(wgpu_types::Features::SHADER_F16) { - return_features.insert(ShaderF16); - } - // texture formats - if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) { - return_features.insert(Depth32floatStencil8); - } - if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) { - return_features.insert(TextureCompressionBc); - } - if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D) { - return_features.insert(TextureCompressionBcSliced3d); - } - if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) { - return_features.insert(TextureCompressionEtc2); - } - if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) { - return_features.insert(TextureCompressionAstc); - } - if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D) { - return_features.insert(TextureCompressionAstcSliced3d); - } - if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) { - return_features.insert(Rg11b10ufloatRenderable); - } - if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) { - return_features.insert(Bgra8unormStorage); - } - if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) { - return_features.insert(Float32Filterable); - } - if features.contains(wgpu_types::Features::DUAL_SOURCE_BLENDING) { - return_features.insert(DualSourceBlending); - } - if features.contains(wgpu_types::Features::SUBGROUP) { - return_features.insert(Subgroups); - } - - // extended from spec - - // texture formats - if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) { - return_features.insert(TextureFormat16BitNorm); - } - if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) { - return_features.insert(TextureCompressionAstcHdr); - } - if features.contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) { - return_features.insert(TextureAdapterSpecificFormatFeatures); - } - // api - if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) { - return_features.insert(PipelineStatisticsQuery); - } - if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) { - return_features.insert(TimestampQueryInsidePasses); - } - if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) { - return_features.insert(MappablePrimaryBuffers); - } - if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) { - return_features.insert(TextureBindingArray); - } - if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) { - return_features.insert(BufferBindingArray); - } - if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) { - return_features.insert(StorageResourceBindingArray); - } - if features.contains( +pub fn features_to_feature_names( + features: wgpu_types::Features, +) -> HashSet { + use GPUFeatureName::*; + let mut return_features = HashSet::new(); + + // api + if features.contains(wgpu_types::Features::DEPTH_CLIP_CONTROL) { + return_features.insert(DepthClipControl); + } + if features.contains(wgpu_types::Features::TIMESTAMP_QUERY) { + return_features.insert(TimestampQuery); + } + if features.contains(wgpu_types::Features::INDIRECT_FIRST_INSTANCE) { + return_features.insert(IndirectFirstInstance); + } + // shader + if features.contains(wgpu_types::Features::SHADER_F16) { + return_features.insert(ShaderF16); + } + // texture formats + if features.contains(wgpu_types::Features::DEPTH32FLOAT_STENCIL8) { + return_features.insert(Depth32floatStencil8); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC) { + return_features.insert(TextureCompressionBc); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_BC_SLICED_3D) { + return_features.insert(TextureCompressionBcSliced3d); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ETC2) { + return_features.insert(TextureCompressionEtc2); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC) { + return_features.insert(TextureCompressionAstc); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_SLICED_3D) + { + return_features.insert(TextureCompressionAstcSliced3d); + } + if features.contains(wgpu_types::Features::RG11B10UFLOAT_RENDERABLE) { + return_features.insert(Rg11b10ufloatRenderable); + } + if features.contains(wgpu_types::Features::BGRA8UNORM_STORAGE) { + return_features.insert(Bgra8unormStorage); + } + if features.contains(wgpu_types::Features::FLOAT32_FILTERABLE) { + return_features.insert(Float32Filterable); + } + if features.contains(wgpu_types::Features::DUAL_SOURCE_BLENDING) { + return_features.insert(DualSourceBlending); + } + if features.contains(wgpu_types::Features::SUBGROUP) { + return_features.insert(Subgroups); + } + + // extended from spec + + // texture formats + if features.contains(wgpu_types::Features::TEXTURE_FORMAT_16BIT_NORM) { + return_features.insert(TextureFormat16BitNorm); + } + if features.contains(wgpu_types::Features::TEXTURE_COMPRESSION_ASTC_HDR) { + return_features.insert(TextureCompressionAstcHdr); + } + if features + .contains(wgpu_types::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) + { + return_features.insert(TextureAdapterSpecificFormatFeatures); + } + // api + if features.contains(wgpu_types::Features::PIPELINE_STATISTICS_QUERY) { + return_features.insert(PipelineStatisticsQuery); + } + if features.contains(wgpu_types::Features::TIMESTAMP_QUERY_INSIDE_PASSES) { + return_features.insert(TimestampQueryInsidePasses); + } + if features.contains(wgpu_types::Features::MAPPABLE_PRIMARY_BUFFERS) { + return_features.insert(MappablePrimaryBuffers); + } + if features.contains(wgpu_types::Features::TEXTURE_BINDING_ARRAY) { + return_features.insert(TextureBindingArray); + } + if features.contains(wgpu_types::Features::BUFFER_BINDING_ARRAY) { + return_features.insert(BufferBindingArray); + } + if features.contains(wgpu_types::Features::STORAGE_RESOURCE_BINDING_ARRAY) { + return_features.insert(StorageResourceBindingArray); + } + if features.contains( wgpu_types::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, ) { return_features.insert(SampledTextureAndStorageBufferArrayNonUniformIndexing); } - if features.contains(wgpu_types::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING) { - return_features.insert(StorageTextureArrayNonUniformIndexing); - } - if features.contains(wgpu_types::Features::UNIFORM_BUFFER_BINDING_ARRAYS) { - return_features.insert(UniformBufferBindingArrays); - } - if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) { - return_features.insert(PartiallyBoundBindingArray); - } - if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT) { - return_features.insert(MultiDrawIndirect); - } - if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) { - return_features.insert(MultiDrawIndirectCount); - } - if features.contains(wgpu_types::Features::PUSH_CONSTANTS) { - return_features.insert(PushConstants); - } - if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) { - return_features.insert(AddressModeClampToZero); - } - if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) { - return_features.insert(AddressModeClampToBorder); - } - if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) { - return_features.insert(PolygonModeLine); - } - if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) { - return_features.insert(PolygonModePoint); - } - if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) { - return_features.insert(ConservativeRasterization); - } - if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) { - return_features.insert(VertexWritableStorage); - } - if features.contains(wgpu_types::Features::CLEAR_TEXTURE) { - return_features.insert(ClearTexture); - } - if features.contains(wgpu_types::Features::SPIRV_SHADER_PASSTHROUGH) { - return_features.insert(SpirvShaderPassthrough); - } - if features.contains(wgpu_types::Features::MULTIVIEW) { - return_features.insert(Multiview); - } - if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) { - return_features.insert(VertexAttribute64Bit); - } - // shader - if features.contains(wgpu_types::Features::SHADER_F64) { - return_features.insert(ShaderF64); - } - if features.contains(wgpu_types::Features::SHADER_I16) { - return_features.insert(ShaderI16); - } - if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) { - return_features.insert(ShaderPrimitiveIndex); - } - if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) { - return_features.insert(ShaderEarlyDepthTest); - } - - return_features + if features + .contains(wgpu_types::Features::STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING) + { + return_features.insert(StorageTextureArrayNonUniformIndexing); + } + if features.contains(wgpu_types::Features::UNIFORM_BUFFER_BINDING_ARRAYS) { + return_features.insert(UniformBufferBindingArrays); + } + if features.contains(wgpu_types::Features::PARTIALLY_BOUND_BINDING_ARRAY) { + return_features.insert(PartiallyBoundBindingArray); + } + if features.contains(wgpu_types::Features::MULTI_DRAW_INDIRECT_COUNT) { + return_features.insert(MultiDrawIndirectCount); + } + if features.contains(wgpu_types::Features::PUSH_CONSTANTS) { + return_features.insert(PushConstants); + } + if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_ZERO) { + return_features.insert(AddressModeClampToZero); + } + if features.contains(wgpu_types::Features::ADDRESS_MODE_CLAMP_TO_BORDER) { + return_features.insert(AddressModeClampToBorder); + } + if features.contains(wgpu_types::Features::POLYGON_MODE_LINE) { + return_features.insert(PolygonModeLine); + } + if features.contains(wgpu_types::Features::POLYGON_MODE_POINT) { + return_features.insert(PolygonModePoint); + } + if features.contains(wgpu_types::Features::CONSERVATIVE_RASTERIZATION) { + return_features.insert(ConservativeRasterization); + } + if features.contains(wgpu_types::Features::VERTEX_WRITABLE_STORAGE) { + return_features.insert(VertexWritableStorage); + } + if features.contains(wgpu_types::Features::CLEAR_TEXTURE) { + return_features.insert(ClearTexture); + } + if features.contains(wgpu_types::Features::MULTIVIEW) { + return_features.insert(Multiview); + } + if features.contains(wgpu_types::Features::VERTEX_ATTRIBUTE_64BIT) { + return_features.insert(VertexAttribute64Bit); + } + // shader + if features.contains(wgpu_types::Features::SHADER_F64) { + return_features.insert(ShaderF64); + } + if features.contains(wgpu_types::Features::SHADER_I16) { + return_features.insert(ShaderI16); + } + if features.contains(wgpu_types::Features::SHADER_PRIMITIVE_INDEX) { + return_features.insert(ShaderPrimitiveIndex); + } + if features.contains(wgpu_types::Features::SHADER_EARLY_DEPTH_TEST) { + return_features.insert(ShaderEarlyDepthTest); + } + if features.contains(wgpu_types::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS) { + return_features.insert(PassthroughShaders); + } + + return_features } diff --git a/docs/api-specs/mesh_shading.md b/docs/api-specs/mesh_shading.md index 8c979890b78..46d7948d55e 100644 --- a/docs/api-specs/mesh_shading.md +++ b/docs/api-specs/mesh_shading.md @@ -2,8 +2,7 @@ 🧪Experimental🧪 -`wgpu` supports an experimental version of mesh shading. The extensions allow for acceleration structures to be created and built (with -`Features::EXPERIMENTAL_MESH_SHADER` enabled) and interacted with in shaders. Currently `naga` has no support for mesh shaders beyond recognizing the additional shader stages. +`wgpu` supports an experimental version of mesh shading. Currently `naga` has no support for mesh shaders beyond recognizing the additional shader stages. For this reason, all shaders must be created with `Device::create_shader_module_passthrough`. **Note**: The features documented here may have major bugs in them and are expected to be subject diff --git a/docs/api-specs/ray_tracing.md b/docs/api-specs/ray_tracing.md index afb447b9eec..a4038547d46 100644 --- a/docs/api-specs/ray_tracing.md +++ b/docs/api-specs/ray_tracing.md @@ -3,7 +3,7 @@ 🧪Experimental🧪 `wgpu` supports an experimental version of ray tracing which is subject to change. The extensions allow for acceleration structures to be created and built (with -`Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE` enabled) and interacted with in shaders. Currently `naga` only supports ray queries +`Features::EXPERIMENTAL_RAY_QUERY` enabled) and interacted with in shaders. Currently `naga` only supports ray queries (accessible with `Features::EXPERIMENTAL_RAY_QUERY` enabled in wgpu). **Note**: The features documented here may have major bugs in them and are expected to be subject @@ -18,6 +18,9 @@ an [introduction](https://developer.nvidia.com/blog/introduction-nvidia-rtx-dire The documentation and specific details of the functions and structures provided can be found with their definitions. +Acceleration structures do not have a separate feature, instead they are enabled by `Features::EXPERIMENTAL_RAY_QUERY`, unlike vulkan. +When ray tracing pipelines are added, that feature will also enable acceleration structures. + A [`Blas`] can be created with [`Device::create_blas`]. A [`Tlas`] can be created with [`Device::create_tlas`]. diff --git a/docs/broadcast_license.nu b/docs/broadcast_license.nu new file mode 100644 index 00000000000..bd676677fc5 --- /dev/null +++ b/docs/broadcast_license.nu @@ -0,0 +1,23 @@ +# Quick maintenance script which broadcasts the license files to all crates +# that are released on crates.io + +# Change to the root of the repository +cd ($env.FILE_PWD | path dirname) + +let crates = [ + "wgpu", + "wgpu-core", + "wgpu-core/platform-deps/apple", + "wgpu-core/platform-deps/emscripten", + "wgpu-core/platform-deps/wasm", + "wgpu-core/platform-deps/windows-linux-android", + "wgpu-hal", + "wgpu-info", + "wgpu-types", + "naga", + "naga-cli", +] + +for crate in $crates { + cp LICENSE.APACHE LICENSE.MIT $"./($crate)/" +} diff --git a/docs/release-checklist.md b/docs/release-checklist.md index a314e6c70da..4fcc86b8806 100644 --- a/docs/release-checklist.md +++ b/docs/release-checklist.md @@ -13,6 +13,7 @@ Anyone in the @gfx-rs/wgpu team can perform these steps. ## Major Release Process Approx 1 Week Before: + - Determine if `glow` (@groves), `metal-rs` (@gfx-rs/wgpu) or any other dependant crates will need a release. If so, coordinate with their maintainers. - Go through the changelog: - Re-categorize miscategorized items. @@ -21,6 +22,7 @@ Approx 1 Week Before: - Copy-edit the changelog for clarity. Day of Release: + - Update the version number in the root `Cargo.toml` to the new version, this will update all crates to the new version. - Bump the wgpu dependency numbers in the following places: - `Cargo.toml` @@ -34,22 +36,13 @@ Day of Release: - Checkout `trunk` with the merged PR. - Publish! These commands can be pasted directly into your terminal in a single command, and they will publish everything. ```bash - cargo publish -p naga - cargo publish -p naga-cli - cargo publish -p wgpu-types - cargo publish -p wgpu-hal --all-features - cargo publish -p wgpu-core-deps-apple - cargo publish -p wgpu-core-deps-emscripten - cargo publish -p wgpu-core-deps-wasm - cargo publish -p wgpu-core-deps-windows-linux-android - cargo publish -p wgpu-core --all-features - cargo publish -p wgpu - cargo publish -p wgpu-info + cargo +stable publish --workspace --exclude deno_webgpu ``` - If there were any newly published crates, ensure `github:gfx-rs/wgpu` is added as an owner of that crate. - Create a new tag called `vX.Y.Z` and push it to the repo. - Create a new release on the `wgpu` repo with the changelog from this version, targeting that tag - Create a branch with the with the new version `vX` and push it to the repo. + - On this branch, remove the [!NOTE] at the top of [wgpu/examples/README.md]. - Complete the release's milestone on GitHub. - Create a new milestone for the next release, in 12 weeks time. - Update the release checklist with any needed changes. @@ -66,10 +59,15 @@ Day of Release: - [Rust Community Discord](https://discord.gg/rust-lang-community) in the #games-and-graphics channel ## Patch Release Process -- Enumerate all PRs that haven't been backported yet. These use the `needs-backport` label. [GH Link](https://github.com/gfx-rs/wgpu/pulls?q=sort%3Aupdated-desc+is%3Apr+label%3A%22PR%3A+needs+back-porting%22) + +- Enumerate all PRs that haven't been backported yet. These use the `PR: needs back-porting` label. [GH Link](https://github.com/gfx-rs/wgpu/pulls?q=sort%3Aupdated-desc+is%3Apr+label%3A%22PR%3A+needs+back-porting%22) - On _your own branch_ based on the latest release branch. Cherry-pick the PRs that need to be backported. When modifying the commits, use --append to retain their original authorship. - Remove the `needs-backport` label from the PRs. - Fix the changelogs items and add a new header for the patch release with the release version and date. + - The release section should start with a header saying the following (for example) + ```markdown + This release includes `crate1`, `crate2` and `crate3` version `X.Y.Z`. All other crates remain at their previous versions. + ``` - Once all the PRs are cherry-picked, look at the diff between HEAD and the previous patch release. See what crates changed. - Bump all the versions of the crates that changed. - Create a PR with all of the version changes and changelog updates into the release branch. @@ -77,10 +75,6 @@ Day of Release: - Checkout the release branch with the merged PR. - Publish all relevant crates (see list above). - Create a new release on the `wgpu` repo with the changelog and a tag called `vX.Y.Z` on the release branch. - - The release should start with a header saying the following (for example) - ```markdown - This release includes `crate1`, `crate2` and `crate3` version `X.Y.Z`. All other crates remain at their previous versions. - ``` - Backport the changelog and version bumps to the `trunk` branch. - Ensure that any items in the newly-released changelog don't appear in the "unreleased" section of the trunk changelog. - Update the release checklist with any needed changes. diff --git a/examples/README.md b/examples/README.md index aa083bc390f..ce552d8ebaa 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,10 +1,6 @@ -> [!NOTE] -> These are the examples for the development version of wgpu. If you want to see the examples for the latest crates.io release -> of wgpu, go to the [latest release branch](https://github.com/gfx-rs/wgpu/tree/v26/examples#readme). - # Examples -If you are just starting your graphics programming journey entirely, we recommend going through [Learn-WGPU](https://sotrh.github.io/learn-wgpu/) +If you are just starting your graphics programming journey entirely, we recommend going through [Learn-wgpu](https://sotrh.github.io/learn-wgpu/) for a mode guided tutorial, which will also teach you the basics of graphics programming. ## Standalone Examples @@ -23,7 +19,7 @@ be cloned out of the repository to serve as a starting point for your own projec You can also use [`cargo-generate`](https://github.com/cargo-generate/cargo-generate) to easily use these as a basis for your own projects. ```sh -cargo generate gfx-rs/wgpu --branch v26 +cargo generate gfx-rs/wgpu --branch v27 ``` ## Framework Examples @@ -32,17 +28,18 @@ These examples use a common framework to handle wgpu init, window creation, and #### Graphics -- `hello_triangle` - Provides an example of a bare-bones WGPU workflow using the Winit crate that simply renders a red triangle on a green background. +- `hello_triangle` - Provides an example of a bare-bones wgpu workflow using the Winit crate that simply renders a red triangle on a green background. - `uniform_values` - Demonstrates the basics of enabling shaders and the GPU, in general, to access app state through uniform variables. `uniform_values` also serves as an example of rudimentary app building as the app stores state and takes window-captured keyboard events. The app displays the Mandelbrot Set in grayscale (similar to `storage_texture`) but allows the user to navigate and explore it using their arrow keys and scroll wheel. - `cube` - Introduces the user to slightly more advanced models. The example creates a set of triangles to form a cube on the CPU and then uses a vertex and index buffer to send the generated model to the GPU for usage in rendering. It also uses a texture generated on the CPU to shade the sides of the cube and a uniform variable to apply a transformation matrix to the cube in the shader. - `bunnymark` - Demonstrates many things, but chief among them is performing numerous draw calls with different bind groups in one render pass. The example also uses textures for the icon and uniform buffers to transfer both global and per-particle states. - `skybox` - Shows off too many concepts to list here. The name comes from game development where a "skybox" acts as a background for rendering, usually to add a sky texture for immersion, although they can also be used for backdrops to give the idea of a world beyond the game scene. This example does so much more than this, though, as it uses a car model loaded from a file and uses the user's mouse to rotate the car model in 3d. `skybox` also makes use of depth textures and similar app patterns to `uniform_values`. -- `shadow` - Likely by far the most complex example (certainly the largest in lines of code) of the official WGPU examples. `shadow` demonstrates basic scene rendering with the main attraction being lighting and shadows (as the name implies). It is recommended that any user looking into lighting be very familiar with the basic concepts of not only rendering with WGPU but also the primary mathematical ideas of computer graphics. +- `shadow` - Likely by far the most complex example (certainly the largest in lines of code) of the official wgpu examples. `shadow` demonstrates basic scene rendering with the main attraction being lighting and shadows (as the name implies). It is recommended that any user looking into lighting be very familiar with the basic concepts of not only rendering with wgpu but also the primary mathematical ideas of computer graphics. - `multiple-render-targets` - Demonstrates how to render to two texture targets simultaneously from fragment shader. - `render_to_texture` - Renders to an image texture offscreen, demonstrating both off-screen rendering as well as how to add a sort of resolution-agnostic screenshot feature to an engine. This example either outputs an image file of your naming (pass command line arguments after specifying a `--` like `cargo run --bin wgpu-examples -- render_to_texture "test.png"`) or adds an `img` element containing the image to the page in WASM. - `ray_cube_fragment` - Demonstrates using ray queries with a fragment shader. - `ray_scene` - Demonstrates using ray queries and model loading - `ray_shadows` - Demonstrates a simple use of ray queries - high quality shadows - uses a light set with push constants to raytrace through an untransformed scene and detect whether there is something obstructing the light. +- `mesh_shader` - Renders a triangle to a window with mesh shaders, while showcasing most mesh shader related features(task shaders, payloads, per primitive data). #### Compute diff --git a/examples/features/Cargo.toml b/examples/features/Cargo.toml index b11ba7fc8a7..54998b467e1 100644 --- a/examples/features/Cargo.toml +++ b/examples/features/Cargo.toml @@ -33,12 +33,13 @@ webgpu = ["wgpu/webgpu"] [dependencies] bytemuck.workspace = true cfg-if.workspace = true -encase = { workspace = true, features = ["glam"] } +encase = { workspace = true } flume.workspace = true -glam = { workspace = true, features = ["bytemuck"] } +getrandom = { workspace = true, features = ["wasm_js"] } +glam = { workspace = true, features = ["bytemuck", "encase"] } ktx2.workspace = true log.workspace = true -nanorand = { workspace = true, features = ["tls"] } +nanorand = { workspace = true, features = ["getrandom"] } noise.workspace = true obj.workspace = true png.workspace = true diff --git a/examples/features/src/big_compute_buffers/mod.rs b/examples/features/src/big_compute_buffers/mod.rs index 90561271851..7a4f658f73a 100644 --- a/examples/features/src/big_compute_buffers/mod.rs +++ b/examples/features/src/big_compute_buffers/mod.rs @@ -80,7 +80,7 @@ pub async fn execute_gpu_inner( slice.map_async(wgpu::MapMode::Read, |_| {}); } - device.poll(wgpu::PollType::Wait).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); let mut data = Vec::new(); for staging_buffer in &staging_buffers { @@ -193,7 +193,7 @@ fn create_storage_buffers(device: &wgpu::Device, numbers: &[f32]) -> Vec Vec Option<&Surface> { + fn get(&self) -> Option<&'_ Surface<'static>> { self.surface.as_ref() } @@ -268,9 +268,9 @@ impl ExampleContext { async fn init_async(surface: &mut SurfaceWrapper, window: Arc) -> Self { log::info!("Initializing wgpu..."); - let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor::from_env_or_default()); + let instance_descriptor = wgpu::InstanceDescriptor::from_env_or_default(); + let instance = wgpu::Instance::new(&instance_descriptor); surface.pre_adapter(&instance, window); - let adapter = get_adapter_with_capabilities_or_from_env( &instance, &E::required_features(), @@ -287,6 +287,7 @@ impl ExampleContext { required_features: (E::optional_features() & adapter.features()) | E::required_features(), required_limits: needed_limits, + experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }, memory_hints: wgpu::MemoryHints::MemoryUsage, trace: match std::env::var_os("WGPU_TRACE") { Some(path) => wgpu::Trace::Directory(path.into()), @@ -328,7 +329,7 @@ impl FrameCounter { let elapsed_ms = elapsed_secs * 1000.0; let frame_time = elapsed_ms / self.frame_count as f32; let fps = self.frame_count as f32 / elapsed_secs; - log::info!("Frame time {:.2}ms ({:.1} FPS)", frame_time, fps); + log::info!("Frame time {frame_time:.2}ms ({fps:.1} FPS)"); self.last_printed_instant = new_instant; self.frame_count = 0; @@ -596,7 +597,9 @@ impl From> let dst_buffer_slice = dst_buffer.slice(..); dst_buffer_slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let bytes = dst_buffer_slice.get_mapped_range().to_vec(); wgpu_test::image::compare_image_output( diff --git a/examples/features/src/hello_synchronization/mod.rs b/examples/features/src/hello_synchronization/mod.rs index 41d51acd599..ea41e4944b7 100644 --- a/examples/features/src/hello_synchronization/mod.rs +++ b/examples/features/src/hello_synchronization/mod.rs @@ -16,6 +16,7 @@ async fn run() { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::Performance, trace: wgpu::Trace::Off, }) @@ -28,13 +29,13 @@ async fn run() { } = execute(&device, &queue, ARR_SIZE).await; // Print data - log::info!("Patient results: {:?}", patient_workgroup_results); + log::info!("Patient results: {patient_workgroup_results:?}"); if !patient_workgroup_results.iter().any(|e| *e != 16) { log::info!("patient_main was patient."); } else { log::error!("patient_main was not patient!"); } - log::info!("Hasty results: {:?}", hasty_workgroup_results); + log::info!("Hasty results: {hasty_workgroup_results:?}"); if hasty_workgroup_results.iter().any(|e| *e != 16) { log::info!("hasty_main was not patient."); } else { @@ -181,7 +182,7 @@ async fn get_data( let buffer_slice = staging_buffer.slice(..); let (sender, receiver) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap()); - device.poll(wgpu::PollType::wait()).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); receiver.recv_async().await.unwrap().unwrap(); output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..])); staging_buffer.unmap(); @@ -208,4 +209,4 @@ pub fn main() { } #[cfg(test)] -mod tests; +pub mod tests; diff --git a/examples/features/src/hello_synchronization/tests.rs b/examples/features/src/hello_synchronization/tests.rs index 756289a3637..ff34abbd091 100644 --- a/examples/features/src/hello_synchronization/tests.rs +++ b/examples/features/src/hello_synchronization/tests.rs @@ -2,7 +2,7 @@ use super::*; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; #[gpu_test] -static SYNC: GpuTestConfiguration = GpuTestConfiguration::new() +pub static SYNC: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( // Taken from hello-compute tests. TestParameters::default() diff --git a/examples/features/src/hello_triangle/mod.rs b/examples/features/src/hello_triangle/mod.rs index 89ef9864f5b..eca99f6bb37 100644 --- a/examples/features/src/hello_triangle/mod.rs +++ b/examples/features/src/hello_triangle/mod.rs @@ -31,6 +31,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) { // Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain. required_limits: wgpu::Limits::downlevel_webgl2_defaults() .using_resolution(adapter.limits()), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) diff --git a/examples/features/src/hello_windows/mod.rs b/examples/features/src/hello_windows/mod.rs index 9885d240221..fbd69f0bff2 100644 --- a/examples/features/src/hello_windows/mod.rs +++ b/examples/features/src/hello_windows/mod.rs @@ -74,6 +74,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc, wgpu::Color label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) diff --git a/examples/features/src/hello_workgroups/mod.rs b/examples/features/src/hello_workgroups/mod.rs index 5a8e7ffab0a..a3f3885a9ce 100644 --- a/examples/features/src/hello_workgroups/mod.rs +++ b/examples/features/src/hello_workgroups/mod.rs @@ -31,6 +31,7 @@ async fn run() { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) @@ -170,7 +171,7 @@ async fn get_data( let buffer_slice = staging_buffer.slice(..); let (sender, receiver) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap()); - device.poll(wgpu::PollType::wait()).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); receiver.recv_async().await.unwrap().unwrap(); output.copy_from_slice(bytemuck::cast_slice(&buffer_slice.get_mapped_range()[..])); staging_buffer.unmap(); diff --git a/examples/features/src/lib.rs b/examples/features/src/lib.rs index 479f1d12e9a..baacf6a6b39 100644 --- a/examples/features/src/lib.rs +++ b/examples/features/src/lib.rs @@ -13,6 +13,7 @@ pub mod hello_synchronization; pub mod hello_triangle; pub mod hello_windows; pub mod hello_workgroups; +pub mod mesh_shader; pub mod mipmap; pub mod msaa_line; pub mod multiple_render_targets; @@ -35,4 +36,52 @@ pub mod uniform_values; pub mod water; #[cfg(test)] -wgpu_test::gpu_test_main!(); +fn all_tests() -> Vec { + #[cfg_attr( + target_arch = "wasm32", + expect(unused_mut, reason = "non-wasm32 needs this mutable") + )] + let mut test_list = vec![ + boids::TEST, + bunnymark::TEST, + conservative_raster::TEST, + cube::TEST, + cube::TEST_LINES, + hello_synchronization::tests::SYNC, + mipmap::TEST, + mipmap::TEST_QUERY, + msaa_line::TEST, + multiple_render_targets::TEST, + ray_cube_compute::TEST, + ray_cube_fragment::TEST, + ray_cube_normals::TEST, + ray_scene::TEST, + ray_shadows::TEST, + ray_traced_triangle::TEST, + shadow::TEST, + skybox::TEST, + skybox::TEST_ASTC, + skybox::TEST_BCN, + skybox::TEST_ETC2, + srgb_blend::TEST_LINEAR, + srgb_blend::TEST_SRGB, + stencil_triangles::TEST, + texture_arrays::TEST, + texture_arrays::TEST_NON_UNIFORM, + texture_arrays::TEST_UNIFORM, + timestamp_queries::tests::TIMESTAMPS_ENCODER, + timestamp_queries::tests::TIMESTAMPS_PASSES, + timestamp_queries::tests::TIMESTAMPS_PASS_BOUNDARIES, + water::TEST, + ]; + + #[cfg(not(target_arch = "wasm32"))] + { + test_list.push(big_compute_buffers::tests::TWO_BUFFERS); + } + + test_list +} + +#[cfg(test)] +wgpu_test::gpu_test_main!(all_tests()); diff --git a/examples/features/src/main.rs b/examples/features/src/main.rs index b90c90d26cf..c9fc31259c9 100644 --- a/examples/features/src/main.rs +++ b/examples/features/src/main.rs @@ -182,6 +182,12 @@ const EXAMPLES: &[ExampleDesc] = &[ webgl: false, // No Ray-tracing extensions webgpu: false, // No Ray-tracing extensions (yet) }, + ExampleDesc { + name: "mesh_shader", + function: wgpu_examples::mesh_shader::main, + webgl: false, + webgpu: false, + }, ]; fn get_example_name() -> Option { diff --git a/examples/features/src/mesh_shader/README.md b/examples/features/src/mesh_shader/README.md new file mode 100644 index 00000000000..9b57d3e4907 --- /dev/null +++ b/examples/features/src/mesh_shader/README.md @@ -0,0 +1,9 @@ +# mesh_shader + +This example renders a triangle to a window with mesh shaders, while showcasing most mesh shader related features(task shaders, payloads, per primitive data). + +## To Run + +``` +cargo run --bin wgpu-examples mesh_shader +``` \ No newline at end of file diff --git a/examples/features/src/mesh_shader/mod.rs b/examples/features/src/mesh_shader/mod.rs new file mode 100644 index 00000000000..50ddff39a07 --- /dev/null +++ b/examples/features/src/mesh_shader/mod.rs @@ -0,0 +1,180 @@ +use std::process::Stdio; + +// Same as in mesh shader tests +fn compile_glsl(device: &wgpu::Device, shader_stage: &'static str) -> wgpu::ShaderModule { + let cmd = std::process::Command::new("glslc") + .args([ + &format!( + "{}/src/mesh_shader/shader.{shader_stage}", + env!("CARGO_MANIFEST_DIR") + ), + "-o", + "-", + "--target-env=vulkan1.2", + "--target-spv=spv1.4", + ]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to call glslc"); + let output = cmd.wait_with_output().expect("Error waiting for glslc"); + assert!(output.status.success()); + unsafe { + device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { + entry_point: "main".into(), + label: None, + spirv: Some(wgpu::util::make_spirv_raw(&output.stdout)), + ..Default::default() + }) + } +} +fn compile_hlsl(device: &wgpu::Device, entry: &str, stage_str: &str) -> wgpu::ShaderModule { + let out_path = format!( + "{}/src/mesh_shader/shader.{stage_str}.cso", + env!("CARGO_MANIFEST_DIR") + ); + let cmd = std::process::Command::new("dxc") + .args([ + "-T", + &format!("{stage_str}_6_5"), + "-E", + entry, + &format!("{}/src/mesh_shader/shader.hlsl", env!("CARGO_MANIFEST_DIR")), + "-Fo", + &out_path, + ]) + .output() + .unwrap(); + if !cmd.status.success() { + panic!("DXC failed:\n{}", String::from_utf8(cmd.stderr).unwrap()); + } + let file = std::fs::read(&out_path).unwrap(); + std::fs::remove_file(out_path).unwrap(); + unsafe { + device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { + entry_point: entry.to_owned(), + label: None, + num_workgroups: (1, 1, 1), + dxil: Some(std::borrow::Cow::Owned(file)), + ..Default::default() + }) + } +} + +pub struct Example { + pipeline: wgpu::RenderPipeline, +} +impl crate::framework::Example for Example { + fn init( + config: &wgpu::SurfaceConfiguration, + adapter: &wgpu::Adapter, + device: &wgpu::Device, + _queue: &wgpu::Queue, + ) -> Self { + let (ts, ms, fs) = if adapter.get_info().backend == wgpu::Backend::Vulkan { + ( + compile_glsl(device, "task"), + compile_glsl(device, "mesh"), + compile_glsl(device, "frag"), + ) + } else if adapter.get_info().backend == wgpu::Backend::Dx12 { + ( + compile_hlsl(device, "Task", "as"), + compile_hlsl(device, "Mesh", "ms"), + compile_hlsl(device, "Frag", "ps"), + ) + } else { + panic!("Example can only run on vulkan or dx12"); + }; + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor { + label: None, + layout: Some(&pipeline_layout), + task: Some(wgpu::TaskState { + module: &ts, + entry_point: Some("main"), + compilation_options: Default::default(), + }), + mesh: wgpu::MeshState { + module: &ms, + entry_point: Some("main"), + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &fs, + entry_point: Some("main"), + compilation_options: Default::default(), + targets: &[Some(config.view_formats[0].into())], + }), + primitive: wgpu::PrimitiveState { + cull_mode: Some(wgpu::Face::Back), + ..Default::default() + }, + depth_stencil: None, + multisample: Default::default(), + multiview: None, + cache: None, + }); + Self { pipeline } + } + fn render(&mut self, view: &wgpu::TextureView, device: &wgpu::Device, queue: &wgpu::Queue) { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + view, + resolve_target: None, + ops: wgpu::Operations { + load: wgpu::LoadOp::Clear(wgpu::Color { + r: 0.1, + g: 0.2, + b: 0.3, + a: 1.0, + }), + store: wgpu::StoreOp::Store, + }, + depth_slice: None, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.push_debug_group("Prepare data for draw."); + rpass.set_pipeline(&self.pipeline); + rpass.pop_debug_group(); + rpass.insert_debug_marker("Draw!"); + rpass.draw_mesh_tasks(1, 1, 1); + } + queue.submit(Some(encoder.finish())); + } + fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { + Default::default() + } + fn required_features() -> wgpu::Features { + wgpu::Features::EXPERIMENTAL_MESH_SHADER | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS + } + fn required_limits() -> wgpu::Limits { + wgpu::Limits::defaults().using_recommended_minimum_mesh_shader_values() + } + fn resize( + &mut self, + _config: &wgpu::SurfaceConfiguration, + _device: &wgpu::Device, + _queue: &wgpu::Queue, + ) { + // empty + } + fn update(&mut self, _event: winit::event::WindowEvent) { + // empty + } +} + +pub fn main() { + crate::framework::run::("mesh_shader"); +} diff --git a/examples/features/src/mesh_shader/shader.frag b/examples/features/src/mesh_shader/shader.frag new file mode 100644 index 00000000000..49624990f16 --- /dev/null +++ b/examples/features/src/mesh_shader/shader.frag @@ -0,0 +1,11 @@ +#version 450 +#extension GL_EXT_mesh_shader : require + +in VertexInput { layout(location = 0) vec4 color; } +vertexInput; +layout(location = 1) perprimitiveEXT in PrimitiveInput { vec4 colorMask; } +primitiveInput; + +layout(location = 0) out vec4 fragColor; + +void main() { fragColor = vertexInput.color * primitiveInput.colorMask; } \ No newline at end of file diff --git a/examples/features/src/mesh_shader/shader.hlsl b/examples/features/src/mesh_shader/shader.hlsl new file mode 100644 index 00000000000..e70961f2d71 --- /dev/null +++ b/examples/features/src/mesh_shader/shader.hlsl @@ -0,0 +1,53 @@ +struct OutVertex { + float4 Position : SV_POSITION; + float4 Color: COLOR; +}; +struct OutPrimitive { + float4 ColorMask : COLOR_MASK : PRIMITIVE; + bool CullPrimitive: SV_CullPrimitive; +}; +struct InVertex { + float4 Color: COLOR; +}; +struct InPrimitive { + float4 ColorMask : COLOR_MASK : PRIMITIVE; +}; +struct PayloadData { + float4 ColorMask; + bool Visible; +}; + + +static const float4 positions[3] = {float4(0., 1.0, 0., 1.0), float4(-1.0, -1.0, 0., 1.0), float4(1.0, -1.0, 0., 1.0)}; +static const float4 colors[3] = {float4(0., 1., 0., 1.), float4(0., 0., 1., 1.), float4(1., 0., 0., 1.)}; + +groupshared PayloadData outPayload; + +[numthreads(1, 1, 1)] +void Task() { + outPayload.ColorMask = float4(1.0, 1.0, 0.0, 1.0); + outPayload.Visible = true; + DispatchMesh(3, 1, 1, outPayload); +} + +[outputtopology("triangle")] +[numthreads(1, 1, 1)] +void Mesh(out indices uint3 triangles[1], out vertices OutVertex vertices[3], out primitives OutPrimitive primitives[1], in payload PayloadData payload) { + SetMeshOutputCounts(3, 1); + + vertices[0].Position = positions[0]; + vertices[1].Position = positions[1]; + vertices[2].Position = positions[2]; + + vertices[0].Color = colors[0] * payload.ColorMask; + vertices[1].Color = colors[1] * payload.ColorMask; + vertices[2].Color = colors[2] * payload.ColorMask; + + triangles[0] = uint3(0, 1, 2); + primitives[0].ColorMask = float4(1.0, 0.0, 0.0, 1.0); + primitives[0].CullPrimitive = !payload.Visible; +} + +float4 Frag(InVertex vertex, InPrimitive primitive) : SV_Target { + return vertex.Color * primitive.ColorMask; +} diff --git a/examples/features/src/mesh_shader/shader.mesh b/examples/features/src/mesh_shader/shader.mesh new file mode 100644 index 00000000000..c2579670d30 --- /dev/null +++ b/examples/features/src/mesh_shader/shader.mesh @@ -0,0 +1,38 @@ +#version 450 +#extension GL_EXT_mesh_shader : require + +const vec4[3] positions = {vec4(0., 1.0, 0., 1.0), vec4(-1.0, -1.0, 0., 1.0), + vec4(1.0, -1.0, 0., 1.0)}; +const vec4[3] colors = {vec4(0., 1., 0., 1.), vec4(0., 0., 1., 1.), + vec4(1., 0., 0., 1.)}; + +// This is an inefficient workgroup size.Ideally the total thread count would be +// a multiple of 64 +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; +struct PayloadData { + vec4 colorMask; + bool visible; +}; +taskPayloadSharedEXT PayloadData payloadData; + +out VertexOutput { layout(location = 0) vec4 color; } +vertexOutput[]; +layout(location = 1) perprimitiveEXT out PrimitiveOutput { vec4 colorMask; } +primitiveOutput[]; + +layout(triangles, max_vertices = 3, max_primitives = 1) out; +void main() { + SetMeshOutputsEXT(3, 1); + + gl_MeshVerticesEXT[0].gl_Position = positions[0]; + gl_MeshVerticesEXT[1].gl_Position = positions[1]; + gl_MeshVerticesEXT[2].gl_Position = positions[2]; + + vertexOutput[0].color = colors[0] * payloadData.colorMask; + vertexOutput[1].color = colors[1] * payloadData.colorMask; + vertexOutput[2].color = colors[2] * payloadData.colorMask; + + gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2); + primitiveOutput[0].colorMask = vec4(1.0, 0.0, 1.0, 1.0); + gl_MeshPrimitivesEXT[0].gl_CullPrimitiveEXT = !payloadData.visible; +} \ No newline at end of file diff --git a/examples/features/src/mesh_shader/shader.task b/examples/features/src/mesh_shader/shader.task new file mode 100644 index 00000000000..04cdef2d5c2 --- /dev/null +++ b/examples/features/src/mesh_shader/shader.task @@ -0,0 +1,16 @@ +#version 450 +#extension GL_EXT_mesh_shader : require + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +struct TaskPayload { + vec4 colorMask; + bool visible; +}; +taskPayloadSharedEXT TaskPayload taskPayload; + +void main() { + taskPayload.colorMask = vec4(1.0, 1.0, 0.0, 1.0); + taskPayload.visible = true; + EmitMeshTasksEXT(3, 1, 1); +} \ No newline at end of file diff --git a/examples/features/src/mipmap/mod.rs b/examples/features/src/mipmap/mod.rs index f9d4febafa0..3875bc3a76a 100644 --- a/examples/features/src/mipmap/mod.rs +++ b/examples/features/src/mipmap/mod.rs @@ -411,7 +411,7 @@ impl crate::framework::Example for Example { .slice(..) .map_async(wgpu::MapMode::Read, |_| ()); // Wait for device to be done rendering mipmaps - device.poll(wgpu::PollType::wait()).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); // This is guaranteed to be ready. let timestamp_view = query_sets .mapping_buffer @@ -508,7 +508,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "mipmap", image_path: "/examples/features/src/mipmap/screenshot.png", width: 1024, @@ -521,7 +521,7 @@ static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTest #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_QUERY: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST_QUERY: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "mipmap-query", image_path: "/examples/features/src/mipmap/screenshot_query.png", width: 1024, diff --git a/examples/features/src/msaa_line/mod.rs b/examples/features/src/msaa_line/mod.rs index 6a8e8626885..71c6b277c4b 100644 --- a/examples/features/src/msaa_line/mod.rs +++ b/examples/features/src/msaa_line/mod.rs @@ -47,7 +47,7 @@ impl Example { vertex_buffer: &wgpu::Buffer, vertex_count: u32, ) -> wgpu::RenderBundle { - log::info!("sample_count: {}", sample_count); + log::info!("sample_count: {sample_count}"); let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor { label: None, layout: Some(pipeline_layout), @@ -321,7 +321,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "msaa-line", image_path: "/examples/features/src/msaa_line/screenshot.png", width: 1024, diff --git a/examples/features/src/multiple_render_targets/mod.rs b/examples/features/src/multiple_render_targets/mod.rs index 61726c3fb1c..d708f701f0b 100644 --- a/examples/features/src/multiple_render_targets/mod.rs +++ b/examples/features/src/multiple_render_targets/mod.rs @@ -536,7 +536,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: EXAMPLE_NAME, image_path: "/examples/features/src/multiple_render_targets/screenshot.png", width: 1024, diff --git a/examples/features/src/ray_cube_compute/mod.rs b/examples/features/src/ray_cube_compute/mod.rs index 1f049a34b9f..3d875b1c745 100644 --- a/examples/features/src/ray_cube_compute/mod.rs +++ b/examples/features/src/ray_cube_compute/mod.rs @@ -114,7 +114,7 @@ impl>> Future for ErrorFuture { let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; inner.poll(cx).map(|error| { if let Some(e) = error { - panic!("Rendering {}", e); + panic!("Rendering {e}"); } }) } @@ -145,7 +145,6 @@ impl crate::framework::Example for Example { wgpu::Features::TEXTURE_BINDING_ARRAY | wgpu::Features::VERTEX_WRITABLE_STORAGE | wgpu::Features::EXPERIMENTAL_RAY_QUERY - | wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE } fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { @@ -487,7 +486,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "ray_cube_compute", image_path: "/examples/features/src/ray_cube_compute/screenshot.png", width: 1024, diff --git a/examples/features/src/ray_cube_fragment/mod.rs b/examples/features/src/ray_cube_fragment/mod.rs index 5c6b2d79e8e..dde26e02832 100644 --- a/examples/features/src/ray_cube_fragment/mod.rs +++ b/examples/features/src/ray_cube_fragment/mod.rs @@ -89,7 +89,7 @@ impl>> Future for ErrorFuture { let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; inner.poll(cx).map(|error| { if let Some(e) = error { - panic!("Rendering {}", e); + panic!("Rendering {e}"); } }) } @@ -108,7 +108,6 @@ struct Example { impl crate::framework::Example for Example { fn required_features() -> wgpu::Features { wgpu::Features::EXPERIMENTAL_RAY_QUERY - | wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE } fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { @@ -373,7 +372,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "ray_cube_fragment", image_path: "/examples/features/src/ray_cube_fragment/screenshot.png", width: 1024, diff --git a/examples/features/src/ray_cube_normals/mod.rs b/examples/features/src/ray_cube_normals/mod.rs index 72985c3b8b4..4620b7551c0 100644 --- a/examples/features/src/ray_cube_normals/mod.rs +++ b/examples/features/src/ray_cube_normals/mod.rs @@ -113,7 +113,7 @@ impl>> Future for ErrorFuture { let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; inner.poll(cx).map(|error| { if let Some(e) = error { - panic!("Rendering {}", e); + panic!("Rendering {e}"); } }) } @@ -133,9 +133,7 @@ impl crate::framework::Example for Example { // Don't want srgb, so normals show up better. const SRGB: bool = false; fn required_features() -> wgpu::Features { - wgpu::Features::EXPERIMENTAL_RAY_QUERY - | wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN + wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN } fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { @@ -474,7 +472,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "ray_cube_normals", image_path: "/examples/features/src/ray_cube_normals/screenshot.png", width: 1024, diff --git a/examples/features/src/ray_scene/mod.rs b/examples/features/src/ray_scene/mod.rs index 6db996e129a..21c2aada802 100644 --- a/examples/features/src/ray_scene/mod.rs +++ b/examples/features/src/ray_scene/mod.rs @@ -41,7 +41,7 @@ impl>> Future for ErrorFuture { let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; inner.poll(cx).map(|error| { if let Some(e) = error { - panic!("Rendering {}", e); + panic!("Rendering {e}"); } }) } @@ -93,7 +93,7 @@ struct Material { fn load_model(scene: &mut RawSceneComponents, path: &str) { let path = env!("CARGO_MANIFEST_DIR").to_string() + "/src" + path; - println!("{}", path); + println!("{path}"); let mut object = obj::Obj::load(path).unwrap(); object.load_mtls().unwrap(); @@ -318,7 +318,6 @@ struct Example { impl crate::framework::Example for Example { fn required_features() -> wgpu::Features { wgpu::Features::EXPERIMENTAL_RAY_QUERY - | wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE } fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { @@ -553,7 +552,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "ray_scene", image_path: "/examples/features/src/ray_scene/screenshot.png", width: 1024, diff --git a/examples/features/src/ray_shadows/mod.rs b/examples/features/src/ray_shadows/mod.rs index 271db01f966..d4d1cabe4b6 100644 --- a/examples/features/src/ray_shadows/mod.rs +++ b/examples/features/src/ray_shadows/mod.rs @@ -68,7 +68,7 @@ impl>> Future for ErrorFuture { let inner = unsafe { self.map_unchecked_mut(|me| &mut me.inner) }; inner.poll(cx).map(|error| { if let Some(e) = error { - panic!("Rendering {}", e); + panic!("Rendering {e}"); } }) } @@ -104,9 +104,7 @@ fn create_matrix(config: &wgpu::SurfaceConfiguration) -> Uniforms { impl crate::framework::Example for Example { fn required_features() -> wgpu::Features { - wgpu::Features::EXPERIMENTAL_RAY_QUERY - | wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::PUSH_CONSTANTS + wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::PUSH_CONSTANTS } fn required_downlevel_capabilities() -> wgpu::DownlevelCapabilities { @@ -362,7 +360,7 @@ impl crate::framework::Example for Example { rpass.draw_indexed(0..12, 0, 0..1); } queue.submit(Some(encoder.finish())); - device.poll(wgpu::PollType::Wait).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); } } @@ -372,7 +370,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "ray_shadows", image_path: "/examples/features/src/ray_shadows/screenshot.png", width: 1024, diff --git a/examples/features/src/ray_traced_triangle/mod.rs b/examples/features/src/ray_traced_triangle/mod.rs index cd0df2c8e5c..0df2e829d2b 100644 --- a/examples/features/src/ray_traced_triangle/mod.rs +++ b/examples/features/src/ray_traced_triangle/mod.rs @@ -29,8 +29,7 @@ struct Uniforms { impl crate::framework::Example for Example { fn required_features() -> wgpu::Features { - wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::EXPERIMENTAL_RAY_QUERY + wgpu::Features::EXPERIMENTAL_RAY_QUERY } fn required_limits() -> wgpu::Limits { @@ -432,7 +431,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "ray_traced_triangle", image_path: "/examples/features/src/ray_traced_triangle/screenshot.png", width: 1024, diff --git a/examples/features/src/render_to_texture/mod.rs b/examples/features/src/render_to_texture/mod.rs index 3460864b004..923434dbae6 100644 --- a/examples/features/src/render_to_texture/mod.rs +++ b/examples/features/src/render_to_texture/mod.rs @@ -20,6 +20,7 @@ async fn run(_path: Option) { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) @@ -131,7 +132,7 @@ async fn run(_path: Option) { let buffer_slice = output_staging_buffer.slice(..); let (sender, receiver) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap()); - device.poll(wgpu::PollType::wait()).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); receiver.recv_async().await.unwrap().unwrap(); log::info!("Output buffer mapped."); { diff --git a/examples/features/src/repeated_compute/mod.rs b/examples/features/src/repeated_compute/mod.rs index 2b9b19995a8..414a53390d1 100644 --- a/examples/features/src/repeated_compute/mod.rs +++ b/examples/features/src/repeated_compute/mod.rs @@ -13,9 +13,11 @@ async fn run() { let mut numbers = [0u32; 256]; let context = WgpuContext::new(size_of_val(&numbers)).await; + let mut rand = nanorand::WyRand::new(); + for _ in 0..10 { for p in numbers.iter_mut() { - *p = nanorand::tls_rng().generate::() as u32; + *p = rand.generate::() as u32; } compute(&mut numbers, &context).await; @@ -103,7 +105,10 @@ async fn compute(local_buffer: &mut [u32], context: &WgpuContext) { // One of those can be calling `Device::poll`. This isn't necessary on the web as devices // are polled automatically but natively, we need to make sure this happens manually. // `PollType::Wait` will cause the thread to wait on native but not on WebGpu. - context.device.poll(wgpu::PollType::wait()).unwrap(); + context + .device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); log::info!("Device polled."); // Now we await the receiving and panic if anything went wrong because we're lazy. receiver.recv_async().await.unwrap().unwrap(); @@ -162,6 +167,7 @@ impl WgpuContext { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::Performance, trace: wgpu::Trace::Off, }) diff --git a/examples/features/src/shadow/mod.rs b/examples/features/src/shadow/mod.rs index 4be97dadde2..b89d2c902dc 100644 --- a/examples/features/src/shadow/mod.rs +++ b/examples/features/src/shadow/mod.rs @@ -839,7 +839,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "shadow", image_path: "/examples/features/src/shadow/screenshot.png", width: 1024, diff --git a/examples/features/src/skybox/mod.rs b/examples/features/src/skybox/mod.rs index 38b0e19066c..699f6615ba9 100644 --- a/examples/features/src/skybox/mod.rs +++ b/examples/features/src/skybox/mod.rs @@ -299,11 +299,7 @@ impl crate::framework::Example for Example { let max_mips = layer_size.max_mips(wgpu::TextureDimension::D2); log::debug!( - "Copying {:?} skybox images of size {}, {}, 6 with {} mips to gpu", - skybox_format, - IMAGE_SIZE, - IMAGE_SIZE, - max_mips, + "Copying {skybox_format:?} skybox images of size {IMAGE_SIZE}, {IMAGE_SIZE}, 6 with {max_mips} mips to gpu", ); let bytes = match skybox_format { @@ -474,7 +470,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "skybox", image_path: "/examples/features/src/skybox/screenshot.png", width: 1024, @@ -489,7 +485,7 @@ static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTest #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_BCN: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST_BCN: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "skybox-bc7", image_path: "/examples/features/src/skybox/screenshot_bc7.png", width: 1024, @@ -502,7 +498,7 @@ static TEST_BCN: crate::framework::ExampleTestParams = crate::framework::Example #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_ETC2: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST_ETC2: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "skybox-etc2", image_path: "/examples/features/src/skybox/screenshot_etc2.png", width: 1024, @@ -515,7 +511,7 @@ static TEST_ETC2: crate::framework::ExampleTestParams = crate::framework::Exampl #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_ASTC: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST_ASTC: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "skybox-astc", image_path: "/examples/features/src/skybox/screenshot_astc.png", width: 1024, diff --git a/examples/features/src/srgb_blend/mod.rs b/examples/features/src/srgb_blend/mod.rs index d91e8cd41ae..fcd59265b76 100644 --- a/examples/features/src/srgb_blend/mod.rs +++ b/examples/features/src/srgb_blend/mod.rs @@ -223,7 +223,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_SRGB: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST_SRGB: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "srgb-blend-srg", // Generated on WARP/Windows image_path: "/examples/features/src/srgb_blend/screenshot-srgb.png", @@ -237,7 +237,7 @@ static TEST_SRGB: crate::framework::ExampleTestParams = crate::framework::Exampl #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_LINEAR: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST_LINEAR: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "srgb-blend-linear", // Generated on WARP/Windows image_path: "/examples/features/src/srgb_blend/screenshot-linear.png", diff --git a/examples/features/src/stencil_triangles/mod.rs b/examples/features/src/stencil_triangles/mod.rs index 5216f225937..e1e44acf40f 100644 --- a/examples/features/src/stencil_triangles/mod.rs +++ b/examples/features/src/stencil_triangles/mod.rs @@ -245,7 +245,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "stencil-triangles", image_path: "/examples/features/src/stencil_triangles/screenshot.png", width: 1024, diff --git a/examples/features/src/storage_texture/mod.rs b/examples/features/src/storage_texture/mod.rs index b028e09a9d1..7db46770ecd 100644 --- a/examples/features/src/storage_texture/mod.rs +++ b/examples/features/src/storage_texture/mod.rs @@ -4,7 +4,7 @@ //! //! Storage textures work like normal textures but they operate similar to storage buffers //! in that they can be written to. The issue is that as it stands, write-only is the -//! only valid access mode for storage textures in WGSL and although there is a WGPU feature +//! only valid access mode for storage textures in WGSL and although there is a wgpu feature //! to allow for read-write access, this is unfortunately a native-only feature and thus //! we won't be using it here. If we needed a reference texture, we would need to add a //! second texture to act as a reference and attach that as well. Luckily, we don't need @@ -34,6 +34,7 @@ async fn run(_path: Option) { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) @@ -141,7 +142,7 @@ async fn run(_path: Option) { let buffer_slice = output_staging_buffer.slice(..); let (sender, receiver) = flume::bounded(1); buffer_slice.map_async(wgpu::MapMode::Read, move |r| sender.send(r).unwrap()); - device.poll(wgpu::PollType::wait()).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); receiver.recv_async().await.unwrap().unwrap(); log::info!("Output buffer mapped"); { diff --git a/examples/features/src/texture_arrays/mod.rs b/examples/features/src/texture_arrays/mod.rs index 1062ff04b90..e7615444200 100644 --- a/examples/features/src/texture_arrays/mod.rs +++ b/examples/features/src/texture_arrays/mod.rs @@ -431,7 +431,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "texture-arrays", image_path: "/examples/features/src/texture_arrays/screenshot.png", width: 1024, @@ -444,20 +444,21 @@ static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTest #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_UNIFORM: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { - name: "texture-arrays-uniform", - image_path: "/examples/features/src/texture_arrays/screenshot.png", - width: 1024, - height: 768, - optional_features: wgpu::Features::empty(), - base_test_parameters: wgpu_test::TestParameters::default(), - comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], - _phantom: std::marker::PhantomData::, -}; +pub static TEST_UNIFORM: crate::framework::ExampleTestParams = + crate::framework::ExampleTestParams { + name: "texture-arrays-uniform", + image_path: "/examples/features/src/texture_arrays/screenshot.png", + width: 1024, + height: 768, + optional_features: wgpu::Features::empty(), + base_test_parameters: wgpu_test::TestParameters::default(), + comparisons: &[wgpu_test::ComparisonType::Mean(0.0)], + _phantom: std::marker::PhantomData::, + }; #[cfg(test)] #[wgpu_test::gpu_test] -static TEST_NON_UNIFORM: crate::framework::ExampleTestParams = +pub static TEST_NON_UNIFORM: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "texture-arrays-non-uniform", image_path: "/examples/features/src/texture_arrays/screenshot.png", diff --git a/examples/features/src/timestamp_queries/mod.rs b/examples/features/src/timestamp_queries/mod.rs index c0fce563d12..9d7b6cbc62f 100644 --- a/examples/features/src/timestamp_queries/mod.rs +++ b/examples/features/src/timestamp_queries/mod.rs @@ -161,7 +161,7 @@ impl Queries { self.destination_buffer .slice(..) .map_async(wgpu::MapMode::Read, |_| ()); - device.poll(wgpu::PollType::wait()).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); let timestamps = { let timestamp_view = self @@ -210,6 +210,7 @@ async fn run() { label: None, required_features: features, required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) @@ -426,13 +427,13 @@ pub fn main() { } #[cfg(test)] -mod tests { +pub mod tests { use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration}; use super::{submit_render_and_compute_pass_with_queries, QueryResults}; #[gpu_test] - static TIMESTAMPS_PASS_BOUNDARIES: GpuTestConfiguration = GpuTestConfiguration::new() + pub static TIMESTAMPS_PASS_BOUNDARIES: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( wgpu_test::TestParameters::default() .limits(wgpu::Limits::downlevel_defaults()) @@ -441,7 +442,7 @@ mod tests { .run_sync(|ctx| test_timestamps(ctx, false, false)); #[gpu_test] - static TIMESTAMPS_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new() + pub static TIMESTAMPS_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( wgpu_test::TestParameters::default() .limits(wgpu::Limits::downlevel_defaults()) @@ -455,7 +456,7 @@ mod tests { .run_sync(|ctx| test_timestamps(ctx, true, false)); #[gpu_test] - static TIMESTAMPS_PASSES: GpuTestConfiguration = GpuTestConfiguration::new() + pub static TIMESTAMPS_PASSES: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( wgpu_test::TestParameters::default() .limits(wgpu::Limits::downlevel_defaults()) diff --git a/examples/features/src/uniform_values/mod.rs b/examples/features/src/uniform_values/mod.rs index 3ee86767255..6a42f2635e1 100644 --- a/examples/features/src/uniform_values/mod.rs +++ b/examples/features/src/uniform_values/mod.rs @@ -18,7 +18,7 @@ use std::sync::Arc; // We won't bring StorageBuffer into scope as that might be too easy to confuse -// with actual GPU-allocated WGPU storage buffers. +// with actual GPU-allocated wgpu storage buffers. use encase::ShaderType; use winit::{ event::{Event, KeyEvent, WindowEvent}, @@ -114,6 +114,7 @@ impl WgpuContext { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) diff --git a/examples/features/src/utils.rs b/examples/features/src/utils.rs index e0c4b4f236a..5d2913c185a 100644 --- a/examples/features/src/utils.rs +++ b/examples/features/src/utils.rs @@ -37,7 +37,7 @@ pub fn output_image_native(image_data: Vec, texture_dims: (usize, usize), pa let mut file = std::fs::File::create(&path).unwrap(); file.write_all(&png_data[..]).unwrap(); - log::info!("PNG file written to disc as \"{}\".", path); + log::info!("PNG file written to disc as \"{path}\"."); } /// Effectively a version of `output_image_native` but meant for web browser contexts. @@ -58,9 +58,8 @@ pub fn output_image_wasm(image_data: Vec, texture_dims: (usize, usize)) { Err(e) => { log::error!( "In searching for a staging canvas for outputting an image \ - (element with id \"staging-canvas\"), found non-canvas element: {:?}. - Replacing with standard staging canvas.", - e + (element with id \"staging-canvas\"), found non-canvas element: {e:?}. + Replacing with standard staging canvas." ); e.remove(); create_staging_canvas(&document) @@ -101,9 +100,8 @@ pub fn output_image_wasm(image_data: Vec, texture_dims: (usize, usize)) { Ok(e) => e, Err(e) => { log::error!( - "Found an element with the id \"output-image-target\" but it was not an image: {:?}. + "Found an element with the id \"output-image-target\" but it was not an image: {e:?}. Replacing with default image output element.", - e ); e.remove(); create_output_image_element(&document) diff --git a/examples/features/src/water/mod.rs b/examples/features/src/water/mod.rs index 08b8f57e015..5d1241fce39 100644 --- a/examples/features/src/water/mod.rs +++ b/examples/features/src/water/mod.rs @@ -823,7 +823,7 @@ pub fn main() { #[cfg(test)] #[wgpu_test::gpu_test] -static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { +pub static TEST: crate::framework::ExampleTestParams = crate::framework::ExampleTestParams { name: "water", image_path: "/examples/features/src/water/screenshot.png", width: 1024, diff --git a/examples/standalone/01_hello_compute/Cargo.toml b/examples/standalone/01_hello_compute/Cargo.toml index 35c61745e6f..a7c36f913a0 100644 --- a/examples/standalone/01_hello_compute/Cargo.toml +++ b/examples/standalone/01_hello_compute/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "wgpu-example-01-hello-compute" edition = "2021" -rust-version = "1.84" +rust-version = "1.88" publish = false [dependencies] bytemuck = "1.22.0" env_logger = "0.11" pollster = "0.4" -wgpu = "26.0.0" +wgpu = "27.0.0" diff --git a/examples/standalone/01_hello_compute/src/main.rs b/examples/standalone/01_hello_compute/src/main.rs index 19d95620c2e..35b6b3c697a 100644 --- a/examples/standalone/01_hello_compute/src/main.rs +++ b/examples/standalone/01_hello_compute/src/main.rs @@ -70,6 +70,7 @@ fn main() { label: None, required_features: wgpu::Features::empty(), required_limits: wgpu::Limits::downlevel_defaults(), + experimental_features: wgpu::ExperimentalFeatures::disabled(), memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, })) @@ -241,7 +242,7 @@ fn main() { // Wait for the GPU to finish working on the submitted work. This doesn't work on WebGPU, so we would need // to rely on the callback to know when the buffer is mapped. - device.poll(wgpu::PollType::Wait).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); // We can now read the data from the buffer. let data = buffer_slice.get_mapped_range(); @@ -249,5 +250,5 @@ fn main() { let result: &[f32] = bytemuck::cast_slice(&data); // Print out the result. - println!("Result: {:?}", result); + println!("Result: {result:?}"); } diff --git a/examples/standalone/02_hello_window/Cargo.toml b/examples/standalone/02_hello_window/Cargo.toml index 35385e6e221..a5f351f95b6 100644 --- a/examples/standalone/02_hello_window/Cargo.toml +++ b/examples/standalone/02_hello_window/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "wgpu-example-02-hello-window" edition = "2021" -rust-version = "1.84" +rust-version = "1.88" publish = false [dependencies] env_logger = "0.11" pollster = "0.4" -wgpu = "26.0.0" +wgpu = "27.0.0" winit = { version = "0.30.8", features = ["android-native-activity"] } diff --git a/examples/standalone/custom_backend/Cargo.toml b/examples/standalone/custom_backend/Cargo.toml index eb0142a1f81..5c05a981ea4 100644 --- a/examples/standalone/custom_backend/Cargo.toml +++ b/examples/standalone/custom_backend/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "wgpu-example-custom-backend" edition = "2021" -rust-version = "1.84" +rust-version = "1.88" publish = false [features] @@ -9,7 +9,7 @@ default = ["web"] web = ["wgpu/web"] [dependencies] -wgpu = { version = "26.0.0", features = [ +wgpu = { version = "27.0.0", features = [ "custom", "wgsl", ], default-features = false } diff --git a/examples/standalone/custom_backend/src/custom.rs b/examples/standalone/custom_backend/src/custom.rs index ec57413ece8..3082be20b5f 100644 --- a/examples/standalone/custom_backend/src/custom.rs +++ b/examples/standalone/custom_backend/src/custom.rs @@ -161,6 +161,13 @@ impl DeviceInterface for CustomDevice { unimplemented!() } + fn create_mesh_pipeline( + &self, + _desc: &wgpu::MeshPipelineDescriptor<'_>, + ) -> wgpu::custom::DispatchRenderPipeline { + unimplemented!() + } + fn create_compute_pipeline( &self, desc: &wgpu::ComputePipelineDescriptor<'_>, @@ -184,6 +191,14 @@ impl DeviceInterface for CustomDevice { unimplemented!() } + fn create_external_texture( + &self, + _desc: &wgpu::ExternalTextureDescriptor<'_>, + _planes: &[&wgpu::TextureView], + ) -> wgpu::custom::DispatchExternalTexture { + unimplemented!() + } + fn create_blas( &self, _desc: &wgpu::CreateBlasDescriptor<'_>, @@ -225,7 +240,7 @@ impl DeviceInterface for CustomDevice { unimplemented!() } - fn on_uncaptured_error(&self, _handler: Box) { + fn on_uncaptured_error(&self, _handler: Arc) { unimplemented!() } diff --git a/naga-cli/Cargo.toml b/naga-cli/Cargo.toml index 16e78631f0b..e1e708ddfad 100644 --- a/naga-cli/Cargo.toml +++ b/naga-cli/Cargo.toml @@ -41,7 +41,7 @@ naga = { workspace = true, features = [ "fs", ] } -bincode.workspace = true +bincode = { workspace = true, features = ["serde"] } codespan-reporting = { workspace = true, default-features = false, features = [ "std", "termcolor", diff --git a/naga-cli/LICENSE.APACHE b/naga-cli/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/naga-cli/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/naga-cli/LICENSE.MIT b/naga-cli/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/naga-cli/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/naga-cli/src/bin/naga.rs b/naga-cli/src/bin/naga.rs index c32d52a8397..b620ecc704f 100644 --- a/naga-cli/src/bin/naga.rs +++ b/naga-cli/src/bin/naga.rs @@ -452,7 +452,7 @@ fn run() -> anyhow::Result<()> { params.spv_in = naga::front::spv::Options { adjust_coordinate_space: !args.keep_coordinate_space, strict_capabilities: false, - block_ctx_dump_prefix: args.block_ctx_dir.clone().map(Into::into), + block_ctx_dump_prefix: args.block_ctx_dir.clone(), }; params.entry_point.clone_from(&args.entry_point); @@ -482,7 +482,7 @@ fn run() -> anyhow::Result<()> { params.compact = args.compact; if args.bulk_validate { - return bulk_validate(args, ¶ms); + return bulk_validate(&args, ¶ms); } let mut files = args.files.iter(); @@ -498,6 +498,8 @@ fn run() -> anyhow::Result<()> { return Err(CliError("Input file path is not specified").into()); }; + let file_name = input_path.to_string_lossy(); + params.input_kind = args.input_kind; params.shader_stage = args.shader_stage; @@ -516,7 +518,7 @@ fn run() -> anyhow::Result<()> { .set(naga::back::spv::WriterFlags::DEBUG, true); params.spv_out.debug_info = Some(naga::back::spv::DebugInfo { source_code: input_text, - file_name: input_path.into(), + file_name: &file_name, language, }) } else { @@ -539,7 +541,8 @@ fn run() -> anyhow::Result<()> { let missing = match Path::new(path).extension().and_then(|ex| ex.to_str()) { Some("wgsl") => C::CLIP_DISTANCE | C::CULL_DISTANCE, Some("metal") => C::CULL_DISTANCE, - _ => C::empty(), + Some("hlsl") => C::empty(), + _ => C::TEXTURE_EXTERNAL, }; caps & !missing }); @@ -643,7 +646,7 @@ fn parse_input(input_path: &Path, input: Vec, params: &Parameters) -> anyhow Ok(match input_kind { InputKind::Bincode => Parsed { - module: bincode::deserialize(&input)?, + module: bincode::serde::decode_from_slice(&input, bincode::config::standard())?.0, input_text: None, language: naga::back::spv::SourceLanguage::Unknown, }, @@ -747,8 +750,8 @@ fn write_output( } } "bin" => { - let file = fs::File::create(output_path)?; - bincode::serialize_into(file, module)?; + let mut file = fs::File::create(output_path)?; + bincode::serde::encode_into_std_write(module, &mut file, bincode::config::standard())?; } "metal" => { use naga::back::msl; @@ -912,9 +915,9 @@ fn write_output( Ok(()) } -fn bulk_validate(args: Args, params: &Parameters) -> anyhow::Result<()> { +fn bulk_validate(args: &Args, params: &Parameters) -> anyhow::Result<()> { let mut invalid = vec![]; - for input_path in args.files { + for input_path in &args.files { let path = Path::new(&input_path); let input = fs::read(path)?; diff --git a/naga-test/Cargo.toml b/naga-test/Cargo.toml new file mode 100644 index 00000000000..54b626e2cfa --- /dev/null +++ b/naga-test/Cargo.toml @@ -0,0 +1,39 @@ +[package] +name = "naga-test" +version.workspace = true +authors.workspace = true +edition.workspace = true +description = "common code for naga tests" +homepage.workspace = true +repository.workspace = true +keywords.workspace = true +license.workspace = true +rust-version.workspace = true +publish = false + + +[features] + +[dependencies] +naga = { workspace = true, features = [ + "serialize", + "deserialize", + "glsl-in", + "glsl-out", + "spv-in", + "spv-out", + "wgsl-in", + "wgsl-out", + "msl-out", + "dot-out", + "hlsl-out", +] } +spirv = { workspace = true, features = ["deserialize"] } +rspirv.workspace = true +ron.workspace = true +toml.workspace = true +bitflags.workspace = true +serde_json.workspace = true +serde.workspace = true +walkdir.workspace = true +env_logger.workspace = true diff --git a/naga-test/src/lib.rs b/naga-test/src/lib.rs new file mode 100644 index 00000000000..078aa27c405 --- /dev/null +++ b/naga-test/src/lib.rs @@ -0,0 +1,453 @@ +// A lot of the code can be unused based on configuration flags, +// the corresponding warnings aren't helpful. +#![allow(dead_code, unused_imports)] + +use core::fmt::Write; + +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use naga::compact::KeepUnused; +use ron::de; + +bitflags::bitflags! { + #[derive(Clone, Copy, serde::Deserialize)] + #[serde(transparent)] + #[derive(Debug, Eq, PartialEq)] + pub struct Targets: u32 { + /// A serialization of the `naga::Module`, in RON format. + const IR = 1; + + /// A serialization of the `naga::valid::ModuleInfo`, in RON format. + const ANALYSIS = 1 << 1; + + const SPIRV = 1 << 2; + const METAL = 1 << 3; + const GLSL = 1 << 4; + const DOT = 1 << 5; + const HLSL = 1 << 6; + const WGSL = 1 << 7; + const NO_VALIDATION = 1 << 8; + } +} + +impl Targets { + /// Defaults for `spv` and `glsl` snapshots. + pub fn non_wgsl_default() -> Self { + Targets::WGSL + } + + /// Defaults for `wgsl` snapshots. + pub fn wgsl_default() -> Self { + Targets::HLSL | Targets::SPIRV | Targets::GLSL | Targets::METAL | Targets::WGSL + } +} + +#[derive(serde::Deserialize)] +pub struct SpvOutVersion(pub u8, pub u8); +impl Default for SpvOutVersion { + fn default() -> Self { + SpvOutVersion(1, 1) + } +} + +#[derive(serde::Deserialize)] +pub struct BindingMapSerialization { + pub resource_binding: naga::ResourceBinding, + pub bind_target: naga::back::spv::BindingInfo, +} + +pub fn deserialize_binding_map<'de, D>( + deserializer: D, +) -> Result +where + D: serde::Deserializer<'de>, +{ + use serde::Deserialize; + + let vec = Vec::::deserialize(deserializer)?; + let mut map = naga::back::spv::BindingMap::default(); + for item in vec { + map.insert(item.resource_binding, item.bind_target); + } + Ok(map) +} + +#[derive(Default, serde::Deserialize)] +#[serde(default)] +pub struct WgslInParameters { + pub parse_doc_comments: bool, +} +impl From<&WgslInParameters> for naga::front::wgsl::Options { + fn from(value: &WgslInParameters) -> Self { + Self { + parse_doc_comments: value.parse_doc_comments, + } + } +} + +#[derive(Default, serde::Deserialize)] +#[serde(default)] +pub struct SpirvInParameters { + pub adjust_coordinate_space: bool, +} +impl From<&SpirvInParameters> for naga::front::spv::Options { + fn from(value: &SpirvInParameters) -> Self { + Self { + adjust_coordinate_space: value.adjust_coordinate_space, + ..Default::default() + } + } +} + +#[derive(serde::Deserialize)] +#[serde(default)] +pub struct SpirvOutParameters { + pub version: SpvOutVersion, + pub capabilities: naga::FastHashSet, + pub debug: bool, + pub adjust_coordinate_space: bool, + pub force_point_size: bool, + pub clamp_frag_depth: bool, + pub separate_entry_points: bool, + #[serde(deserialize_with = "deserialize_binding_map")] + pub binding_map: naga::back::spv::BindingMap, + pub use_storage_input_output_16: bool, +} +impl Default for SpirvOutParameters { + fn default() -> Self { + Self { + version: SpvOutVersion::default(), + capabilities: naga::FastHashSet::default(), + debug: false, + adjust_coordinate_space: false, + force_point_size: false, + clamp_frag_depth: false, + separate_entry_points: false, + use_storage_input_output_16: true, + binding_map: naga::back::spv::BindingMap::default(), + } + } +} +impl SpirvOutParameters { + pub fn to_options<'a>( + &'a self, + bounds_check_policies: naga::proc::BoundsCheckPolicies, + debug_info: Option>, + ) -> naga::back::spv::Options<'a> { + use naga::back::spv; + let mut flags = spv::WriterFlags::LABEL_VARYINGS; + flags.set(spv::WriterFlags::DEBUG, self.debug); + flags.set( + spv::WriterFlags::ADJUST_COORDINATE_SPACE, + self.adjust_coordinate_space, + ); + flags.set(spv::WriterFlags::FORCE_POINT_SIZE, self.force_point_size); + flags.set(spv::WriterFlags::CLAMP_FRAG_DEPTH, self.clamp_frag_depth); + naga::back::spv::Options { + lang_version: (self.version.0, self.version.1), + flags, + capabilities: if self.capabilities.is_empty() { + None + } else { + Some(self.capabilities.clone()) + }, + bounds_check_policies, + fake_missing_bindings: true, + binding_map: self.binding_map.clone(), + zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::Polyfill, + force_loop_bounding: true, + debug_info, + use_storage_input_output_16: self.use_storage_input_output_16, + } + } +} + +#[derive(Default, serde::Deserialize)] +#[serde(default)] +pub struct WgslOutParameters { + pub explicit_types: bool, +} +impl From<&WgslOutParameters> for naga::back::wgsl::WriterFlags { + fn from(value: &WgslOutParameters) -> Self { + let mut flags = Self::empty(); + flags.set(Self::EXPLICIT_TYPES, value.explicit_types); + flags + } +} + +#[derive(Default, serde::Deserialize)] +pub struct FragmentModule { + pub path: String, + pub entry_point: String, +} + +#[derive(Default, serde::Deserialize)] +#[serde(default)] +pub struct Parameters { + // -- GOD MODE -- + pub god_mode: bool, + + // -- wgsl-in options -- + #[serde(rename = "wgsl-in")] + pub wgsl_in: WgslInParameters, + + // -- spirv-in options -- + #[serde(rename = "spv-in")] + pub spv_in: SpirvInParameters, + + // -- SPIR-V options -- + pub spv: SpirvOutParameters, + + /// Defaults to [`Targets::non_wgsl_default()`] for `spv` and `glsl` snapshots, + /// and [`Targets::wgsl_default()`] for `wgsl` snapshots. + pub targets: Option, + + // -- MSL options -- + pub msl: naga::back::msl::Options, + #[serde(default)] + pub msl_pipeline: naga::back::msl::PipelineOptions, + + // -- GLSL options -- + pub glsl: naga::back::glsl::Options, + pub glsl_exclude_list: naga::FastHashSet, + pub glsl_multiview: Option, + + // -- HLSL options -- + pub hlsl: naga::back::hlsl::Options, + + // -- WGSL options -- + pub wgsl: WgslOutParameters, + + // -- General options -- + + // Allow backends to be aware of the fragment module. + // Is the name of a WGSL file in the same directory as the test file. + pub fragment_module: Option, + + pub bounds_check_policies: naga::proc::BoundsCheckPolicies, + pub pipeline_constants: naga::back::PipelineConstants, +} + +/// Information about a shader input file. +#[derive(Debug)] +pub struct Input { + /// The subdirectory of `tests/in` to which this input belongs, if any. + /// + /// If the subdirectory is omitted, we assume that the output goes + /// to "wgsl". + pub subdirectory: PathBuf, + + /// The input filename name, without a directory. + pub file_name: PathBuf, + + /// True if output filenames should add the output extension on top of + /// `file_name`'s existing extension, rather than replacing it. + /// + /// This is used by `convert_snapshots_glsl`, which wants to take input files + /// like `210-bevy-2d-shader.frag` and just add `.wgsl` to it, producing + /// `210-bevy-2d-shader.frag.wgsl`. + pub keep_input_extension: bool, +} + +impl Input { + /// Read an input file and its corresponding parameters file. + /// + /// Given `input`, the relative path of a shader input file, return + /// a `Source` value containing its path, code, and parameters. + /// + /// The `input` path is interpreted relative to the `BASE_DIR_IN` + /// subdirectory of the directory given by the `CARGO_MANIFEST_DIR` + /// environment variable. + pub fn new(subdirectory: &str, name: &str, extension: &str) -> Input { + Input { + subdirectory: PathBuf::from(subdirectory), + // Don't wipe out any extensions on `name`, as + // `with_extension` would do. + file_name: PathBuf::from(format!("{name}.{extension}")), + keep_input_extension: false, + } + } + + /// Return an iterator that produces an `Input` for each entry in `subdirectory`. + pub fn files_in_dir<'a>( + subdirectory: &'a str, + file_extensions: &'a [&'a str], + dir_in: &str, + ) -> impl Iterator + 'a { + let input_directory = Path::new(dir_in).join(subdirectory); + + let entries = match std::fs::read_dir(&input_directory) { + Ok(entries) => entries, + Err(err) => panic!( + "Error opening directory '{}': {}", + input_directory.display(), + err + ), + }; + + entries.filter_map(move |result| { + let entry = result.expect("error reading directory"); + if !entry.file_type().unwrap().is_file() { + return None; + } + + let file_name = PathBuf::from(entry.file_name()); + let extension = file_name + .extension() + .expect("all files in snapshot input directory should have extensions"); + + if !file_extensions.contains(&extension.to_str().unwrap()) { + return None; + } + + if let Ok(pat) = std::env::var("NAGA_SNAPSHOT") { + if !file_name.to_string_lossy().contains(&pat) { + return None; + } + } + + let input = Input::new( + subdirectory, + file_name.file_stem().unwrap().to_str().unwrap(), + extension.to_str().unwrap(), + ); + Some(input) + }) + } + + /// Return the path to the input directory. + pub fn input_directory(&self, dir_in: &str) -> PathBuf { + Path::new(dir_in).join(&self.subdirectory) + } + + /// Return the path to the output directory. + pub fn output_directory(subdirectory: &str, dir_out: &str) -> PathBuf { + Path::new(dir_out).join(subdirectory) + } + + /// Return the path to the input file. + pub fn input_path(&self, dir_in: &str) -> PathBuf { + let mut input = self.input_directory(dir_in); + input.push(&self.file_name); + input + } + + pub fn output_path(&self, subdirectory: &str, extension: &str, dir_out: &str) -> PathBuf { + let mut output = Self::output_directory(subdirectory, dir_out); + if self.keep_input_extension { + let file_name = format!( + "{}-{}.{}", + self.subdirectory.display(), + self.file_name.display(), + extension + ); + + output.push(&file_name); + } else { + let file_name = format!( + "{}-{}", + self.subdirectory.display(), + self.file_name.display() + ); + + output.push(&file_name); + output.set_extension(extension); + } + output + } + + /// Return the contents of the input file as a string. + pub fn read_source(&self, dir_in: &str, print: bool) -> String { + if print { + println!("Processing '{}'", self.file_name.display()); + } + let input_path = self.input_path(dir_in); + match fs::read_to_string(&input_path) { + Ok(source) => source, + Err(err) => { + panic!( + "Couldn't read shader input file `{}`: {}", + input_path.display(), + err + ); + } + } + } + + /// Return the contents of the input file as a vector of bytes. + pub fn read_bytes(&self, dir_in: &str, print: bool) -> Vec { + if print { + println!("Processing '{}'", self.file_name.display()); + } + let input_path = self.input_path(dir_in); + match fs::read(&input_path) { + Ok(bytes) => bytes, + Err(err) => { + panic!( + "Couldn't read shader input file `{}`: {}", + input_path.display(), + err + ); + } + } + } + + pub fn bytes(&self, dir_in: &str) -> u64 { + let input_path = self.input_path(dir_in); + std::fs::metadata(input_path).unwrap().len() + } + + /// Return this input's parameter file, parsed. + pub fn read_parameters(&self, dir_in: &str) -> Parameters { + let mut param_path = self.input_path(dir_in); + param_path.set_extension("toml"); + let mut params = match fs::read_to_string(¶m_path) { + Ok(string) => match toml::de::from_str(&string) { + Ok(params) => params, + Err(e) => panic!( + "Couldn't parse param file: {} due to: {e}", + param_path.display() + ), + }, + Err(_) => Parameters::default(), + }; + + if params.targets.is_none() { + match self + .input_path(dir_in) + .extension() + .unwrap() + .to_str() + .unwrap() + { + "wgsl" => params.targets = Some(Targets::wgsl_default()), + "spvasm" => params.targets = Some(Targets::non_wgsl_default()), + "vert" | "frag" | "comp" => params.targets = Some(Targets::non_wgsl_default()), + e => { + panic!("Unknown extension: {e}"); + } + } + } + + params + } + + /// Write `data` to a file corresponding to this input file in + /// `subdirectory`, with `extension`. + pub fn write_output_file( + &self, + subdirectory: &str, + extension: &str, + data: impl AsRef<[u8]>, + dir_out: &str, + ) { + let output_path = self.output_path(subdirectory, extension, dir_out); + fs::create_dir_all(output_path.parent().unwrap()).unwrap(); + if let Err(err) = fs::write(&output_path, data) { + panic!("Error writing {}: {}", output_path.display(), err); + } + } +} diff --git a/naga/Cargo.toml b/naga/Cargo.toml index 02eda4c198a..0b139f54dcc 100644 --- a/naga/Cargo.toml +++ b/naga/Cargo.toml @@ -113,12 +113,17 @@ env_logger.workspace = true hashbrown = { workspace = true, features = ["serde"] } hlsl-snapshots.workspace = true itertools.workspace = true +naga-test.workspace = true ron.workspace = true rspirv.workspace = true +# So we don't actually need this, however if we remove this, it +# brakes calling `--features spirv` at the workspace level. I think +# this is because there is a `dep:spirv` in the regular feature set, +# so cargo tries to match the feature against that, fails as it's a optional dep, +# and then refuses to build instead of ignoring it. +spirv.workspace = true serde = { workspace = true, features = ["default", "derive"] } -spirv = { workspace = true, features = ["deserialize"] } strum = { workspace = true } -toml.workspace = true walkdir.workspace = true [lints.clippy] diff --git a/naga/LICENSE.APACHE b/naga/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/naga/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/naga/LICENSE.MIT b/naga/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/naga/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/naga/src/arena/unique_arena.rs b/naga/src/arena/unique_arena.rs index 49d481b2c0e..5b9f5297696 100644 --- a/naga/src/arena/unique_arena.rs +++ b/naga/src/arena/unique_arena.rs @@ -72,7 +72,7 @@ impl UniqueArena { .unwrap_or(&Span::default()) } - pub(crate) fn drain_all(&mut self) -> UniqueArenaDrain { + pub(crate) fn drain_all(&mut self) -> UniqueArenaDrain<'_, T> { UniqueArenaDrain { inner_elts: self.set.drain(..), inner_spans: self.span_info.drain(..), diff --git a/naga/src/back/dot/mod.rs b/naga/src/back/dot/mod.rs index c9fc49fe1fc..826dad1c219 100644 --- a/naga/src/back/dot/mod.rs +++ b/naga/src/back/dot/mod.rs @@ -740,7 +740,7 @@ fn write_function_expressions( E::RayQueryVertexPositions { query, committed } => { edges.insert("", query); let ty = if committed { "Committed" } else { "Candidate" }; - (format!("get{}HitVertexPositions", ty).into(), 4) + (format!("get{ty}HitVertexPositions").into(), 4) } }; diff --git a/naga/src/back/glsl/features.rs b/naga/src/back/glsl/features.rs index 1eb3df65f38..a6dfe4e3100 100644 --- a/naga/src/back/glsl/features.rs +++ b/naga/src/back/glsl/features.rs @@ -421,7 +421,8 @@ impl Writer<'_, W> { _ => {} }, ImageClass::Sampled { multi: false, .. } - | ImageClass::Depth { multi: false } => {} + | ImageClass::Depth { multi: false } + | ImageClass::External => {} } } _ => {} diff --git a/naga/src/back/glsl/keywords.rs b/naga/src/back/glsl/keywords.rs index ed217d9e60b..aeaec5b03c0 100644 --- a/naga/src/back/glsl/keywords.rs +++ b/naga/src/back/glsl/keywords.rs @@ -1,7 +1,6 @@ +use crate::proc::KeywordSet; use crate::racy_lock::RacyLock; -use hashbrown::HashSet; - pub const RESERVED_KEYWORDS: &[&str] = &[ // // GLSL 4.6 keywords, from https://github.com/KhronosGroup/OpenGL-Registry/blob/d00e11dc1a1ffba581d633f21f70202051248d5c/specs/gl/GLSLangSpec.4.60.html#L2004-L2322 @@ -499,11 +498,5 @@ pub const RESERVED_KEYWORDS: &[&str] = &[ /// significant time during [`Namer::reset`](crate::proc::Namer::reset). /// /// See for benchmarks. -pub static RESERVED_KEYWORD_SET: RacyLock> = RacyLock::new(|| { - let mut set = HashSet::default(); - set.reserve(RESERVED_KEYWORDS.len()); - for &word in RESERVED_KEYWORDS { - set.insert(word); - } - set -}); +pub static RESERVED_KEYWORD_SET: RacyLock = + RacyLock::new(|| KeywordSet::from_iter(RESERVED_KEYWORDS)); diff --git a/naga/src/back/glsl/mod.rs b/naga/src/back/glsl/mod.rs index ee0daa89c6c..4c5a9d8cbcb 100644 --- a/naga/src/back/glsl/mod.rs +++ b/naga/src/back/glsl/mod.rs @@ -663,7 +663,7 @@ impl<'a, W: Write> Writer<'a, W> { namer.reset( module, &keywords::RESERVED_KEYWORD_SET, - &[], + proc::CaseInsensitiveKeywordSet::empty(), &[ "gl_", // all GL built-in variables "_group", // all normal bindings @@ -1176,6 +1176,7 @@ impl<'a, W: Write> Writer<'a, W> { Ic::Depth { multi: true } => ("sampler", float, "MS", ""), Ic::Depth { multi: false } => ("sampler", float, "", "Shadow"), Ic::Storage { format, .. } => ("image", format.into(), "", ""), + Ic::External => unimplemented!(), }; let precision = if self.options.version.is_es() { @@ -1238,7 +1239,7 @@ impl<'a, W: Write> Writer<'a, W> { write!(self.out, "layout(")?; if let Some(layout) = layout { - write!(self.out, "{}, ", layout)?; + write!(self.out, "{layout}, ")?; } write!(self.out, "binding = {binding}) ")?; @@ -1255,7 +1256,7 @@ impl<'a, W: Write> Writer<'a, W> { // Either no explicit bindings are supported or we didn't have any. // Write just the memory layout. if let Some(layout) = layout { - write!(self.out, "layout({}) ", layout)?; + write!(self.out, "layout({layout}) ")?; } Ok(()) @@ -3302,6 +3303,7 @@ impl<'a, W: Write> Writer<'a, W> { write!(self.out, "imageSize(")?; self.write_expr(image, ctx)?; } + ImageClass::External => unimplemented!(), } write!(self.out, ")")?; if components != 1 || self.options.version.is_es() { @@ -3317,6 +3319,7 @@ impl<'a, W: Write> Writer<'a, W> { let fun_name = match class { ImageClass::Sampled { .. } | ImageClass::Depth { .. } => "textureSize", ImageClass::Storage { .. } => "imageSize", + ImageClass::External => unimplemented!(), }; write!(self.out, "{fun_name}(")?; self.write_expr(image, ctx)?; @@ -3336,6 +3339,7 @@ impl<'a, W: Write> Writer<'a, W> { "textureSamples" } ImageClass::Storage { .. } => "imageSamples", + ImageClass::External => unimplemented!(), }; write!(self.out, "{fun_name}(")?; self.write_expr(image, ctx)?; @@ -3698,11 +3702,11 @@ impl<'a, W: Write> Writer<'a, W> { // `Dot4U8Packed`, the code below only introduces parenthesis around // each factor, which aren't strictly needed because both operands are // baked, but which don't hurt either. - write!(self.out, "bitfieldExtract({}(", conversion)?; + write!(self.out, "bitfieldExtract({conversion}(")?; self.write_expr(arg, ctx)?; write!(self.out, "), {}, 8)", i * 8)?; - write!(self.out, " * bitfieldExtract({}(", conversion)?; + write!(self.out, " * bitfieldExtract({conversion}(")?; self.write_expr(arg1, ctx)?; write!(self.out, "), {}, 8)", i * 8)?; @@ -4618,6 +4622,7 @@ impl<'a, W: Write> Writer<'a, W> { "WGSL `textureLoad` from depth textures is not supported in GLSL".to_string(), )) } + crate::ImageClass::External => unimplemented!(), }; // openGL es doesn't have 1D images so we need workaround it diff --git a/naga/src/back/hlsl/help.rs b/naga/src/back/hlsl/help.rs index f5c9d4b3b97..8083d15e863 100644 --- a/naga/src/back/hlsl/help.rs +++ b/naga/src/back/hlsl/help.rs @@ -33,8 +33,8 @@ use super::{ super::FunctionCtx, writer::{ ABS_FUNCTION, DIV_FUNCTION, EXTRACT_BITS_FUNCTION, F2I32_FUNCTION, F2I64_FUNCTION, - F2U32_FUNCTION, F2U64_FUNCTION, IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION, - INSERT_BITS_FUNCTION, MOD_FUNCTION, NEG_FUNCTION, + F2U32_FUNCTION, F2U64_FUNCTION, IMAGE_LOAD_EXTERNAL_FUNCTION, + IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION, INSERT_BITS_FUNCTION, MOD_FUNCTION, NEG_FUNCTION, }, BackendResult, WrappedType, }; @@ -45,8 +45,14 @@ pub(super) struct WrappedArrayLength { pub(super) writable: bool, } +#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] +pub(super) struct WrappedImageLoad { + pub(super) class: crate::ImageClass, +} + #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] pub(super) struct WrappedImageSample { + pub(super) class: crate::ImageClass, pub(super) clamp_to_edge: bool, } @@ -195,6 +201,11 @@ impl super::Writer<'_, W> { let storage_format_str = format.to_hlsl_str(); write!(self.out, "<{storage_format_str}>")? } + crate::ImageClass::External => { + unreachable!( + "external images should be handled by `write_global_external_texture`" + ); + } } Ok(()) } @@ -253,12 +264,282 @@ impl super::Writer<'_, W> { Ok(()) } + /// Helper function used by [`Self::write_wrapped_image_load_function`] and + /// [`Self::write_wrapped_image_sample_function`] to write the shared YUV + /// to RGB conversion code for external textures. Expects the preceding + /// code to declare the Y component as a `float` variable of name `y`, the + /// UV components as a `float2` variable of name `uv`, and the external + /// texture params as a variable of name `params`. The emitted code will + /// return the result. + fn write_convert_yuv_to_rgb_and_return( + &mut self, + level: crate::back::Level, + y: &str, + uv: &str, + params: &str, + ) -> BackendResult { + let l1 = level; + let l2 = l1.next(); + + // Convert from YUV to non-linear RGB in the source color space. We + // declare our matrices as row_major in HLSL, therefore we must reverse + // the order of this multiplication + writeln!( + self.out, + "{l1}float3 srcGammaRgb = mul(float4({y}, {uv}, 1.0), {params}.yuv_conversion_matrix).rgb;" + )?; + + // Apply the inverse of the source transfer function to convert to + // linear RGB in the source color space. + writeln!( + self.out, + "{l1}float3 srcLinearRgb = srcGammaRgb < {params}.src_tf.k * {params}.src_tf.b ?" + )?; + writeln!(self.out, "{l2}srcGammaRgb / {params}.src_tf.k :")?; + writeln!(self.out, "{l2}pow((srcGammaRgb + {params}.src_tf.a - 1.0) / {params}.src_tf.a, {params}.src_tf.g);")?; + + // Multiply by the gamut conversion matrix to convert to linear RGB in + // the destination color space. We declare our matrices as row_major in + // HLSL, therefore we must reverse the order of this multiplication. + writeln!( + self.out, + "{l1}float3 dstLinearRgb = mul(srcLinearRgb, {params}.gamut_conversion_matrix);" + )?; + + // Finally, apply the dest transfer function to convert to non-linear + // RGB in the destination color space, and return the result. + writeln!( + self.out, + "{l1}float3 dstGammaRgb = dstLinearRgb < {params}.dst_tf.b ?" + )?; + writeln!(self.out, "{l2}{params}.dst_tf.k * dstLinearRgb :")?; + writeln!(self.out, "{l2}{params}.dst_tf.a * pow(dstLinearRgb, 1.0 / {params}.dst_tf.g) - ({params}.dst_tf.a - 1);")?; + + writeln!(self.out, "{l1}return float4(dstGammaRgb, 1.0);")?; + Ok(()) + } + + pub(super) fn write_wrapped_image_load_function( + &mut self, + module: &crate::Module, + load: WrappedImageLoad, + ) -> BackendResult { + match load { + WrappedImageLoad { + class: crate::ImageClass::External, + } => { + let l1 = crate::back::Level(1); + let l2 = l1.next(); + let l3 = l2.next(); + let params_ty_name = &self.names + [&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + writeln!(self.out, "float4 {IMAGE_LOAD_EXTERNAL_FUNCTION}(")?; + writeln!(self.out, "{l1}Texture2D plane0,")?; + writeln!(self.out, "{l1}Texture2D plane1,")?; + writeln!(self.out, "{l1}Texture2D plane2,")?; + writeln!(self.out, "{l1}{params_ty_name} params,")?; + writeln!(self.out, "{l1}uint2 coords)")?; + writeln!(self.out, "{{")?; + writeln!(self.out, "{l1}uint2 plane0_size;")?; + writeln!( + self.out, + "{l1}plane0.GetDimensions(plane0_size.x, plane0_size.y);" + )?; + // Clamp coords to provided size of external texture to prevent OOB read. + // If params.size is zero then clamp to the actual size of the texture. + writeln!( + self.out, + "{l1}uint2 cropped_size = any(params.size) ? params.size : plane0_size;" + )?; + writeln!(self.out, "{l1}coords = min(coords, cropped_size - 1);")?; + + // Apply load transformation. We declare our matrices as row_major in + // HLSL, therefore we must reverse the order of this multiplication + writeln!(self.out, "{l1}float3x2 load_transform = float3x2(")?; + writeln!(self.out, "{l2}params.load_transform_0,")?; + writeln!(self.out, "{l2}params.load_transform_1,")?; + writeln!(self.out, "{l2}params.load_transform_2")?; + writeln!(self.out, "{l1});")?; + writeln!(self.out, "{l1}uint2 plane0_coords = uint2(round(mul(float3(coords, 1.0), load_transform)));")?; + writeln!(self.out, "{l1}if (params.num_planes == 1u) {{")?; + // For single plane, simply read from plane0 + writeln!( + self.out, + "{l2}return plane0.Load(uint3(plane0_coords, 0u));" + )?; + writeln!(self.out, "{l1}}} else {{")?; + + // Chroma planes may be subsampled so we must scale the coords accordingly. + writeln!(self.out, "{l2}uint2 plane1_size;")?; + writeln!( + self.out, + "{l2}plane1.GetDimensions(plane1_size.x, plane1_size.y);" + )?; + writeln!(self.out, "{l2}uint2 plane1_coords = uint2(floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));")?; + + // For multi-plane, read the Y value from plane 0 + writeln!( + self.out, + "{l2}float y = plane0.Load(uint3(plane0_coords, 0u)).x;" + )?; + + writeln!(self.out, "{l2}float2 uv;")?; + writeln!(self.out, "{l2}if (params.num_planes == 2u) {{")?; + // Read UV from interleaved plane 1 + writeln!( + self.out, + "{l3}uv = plane1.Load(uint3(plane1_coords, 0u)).xy;" + )?; + writeln!(self.out, "{l2}}} else {{")?; + // Read U and V from planes 1 and 2 respectively + writeln!(self.out, "{l3}uint2 plane2_size;")?; + writeln!( + self.out, + "{l3}plane2.GetDimensions(plane2_size.x, plane2_size.y);" + )?; + writeln!(self.out, "{l3}uint2 plane2_coords = uint2(floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));")?; + writeln!(self.out, "{l3}uv = float2(plane1.Load(uint3(plane1_coords, 0u)).x, plane2.Load(uint3(plane2_coords, 0u)).x);")?; + writeln!(self.out, "{l2}}}")?; + + self.write_convert_yuv_to_rgb_and_return(l2, "y", "uv", "params")?; + + writeln!(self.out, "{l1}}}")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + _ => {} + } + + Ok(()) + } + pub(super) fn write_wrapped_image_sample_function( &mut self, + module: &crate::Module, sample: WrappedImageSample, ) -> BackendResult { match sample { WrappedImageSample { + class: crate::ImageClass::External, + clamp_to_edge: true, + } => { + let l1 = crate::back::Level(1); + let l2 = l1.next(); + let l3 = l2.next(); + let params_ty_name = &self.names + [&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + writeln!( + self.out, + "float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(" + )?; + writeln!(self.out, "{l1}Texture2D plane0,")?; + writeln!(self.out, "{l1}Texture2D plane1,")?; + writeln!(self.out, "{l1}Texture2D plane2,")?; + writeln!(self.out, "{l1}{params_ty_name} params,")?; + writeln!(self.out, "{l1}SamplerState samp,")?; + writeln!(self.out, "{l1}float2 coords)")?; + writeln!(self.out, "{{")?; + writeln!(self.out, "{l1}float2 plane0_size;")?; + writeln!( + self.out, + "{l1}plane0.GetDimensions(plane0_size.x, plane0_size.y);" + )?; + writeln!(self.out, "{l1}float3x2 sample_transform = float3x2(")?; + writeln!(self.out, "{l2}params.sample_transform_0,")?; + writeln!(self.out, "{l2}params.sample_transform_1,")?; + writeln!(self.out, "{l2}params.sample_transform_2")?; + writeln!(self.out, "{l1});")?; + // Apply sample transformation. We declare our matrices as row_major in + // HLSL, therefore we must reverse the order of this multiplication + writeln!( + self.out, + "{l1}coords = mul(float3(coords, 1.0), sample_transform);" + )?; + // Calculate the sample bounds. The purported size of the texture + // (params.size) is irrelevant here as we are dealing with normalized + // coordinates. Usually we would clamp to (0,0)..(1,1). However, we must + // apply the sample transformation to that, also bearing in mind that it + // may contain a flip on either axis. We calculate and adjust for the + // half-texel separately for each plane as it depends on the actual + // texture size which may vary between planes. + writeln!( + self.out, + "{l1}float2 bounds_min = mul(float3(0.0, 0.0, 1.0), sample_transform);" + )?; + writeln!( + self.out, + "{l1}float2 bounds_max = mul(float3(1.0, 1.0, 1.0), sample_transform);" + )?; + writeln!(self.out, "{l1}float4 bounds = float4(min(bounds_min, bounds_max), max(bounds_min, bounds_max));")?; + writeln!( + self.out, + "{l1}float2 plane0_half_texel = float2(0.5, 0.5) / plane0_size;" + )?; + writeln!( + self.out, + "{l1}float2 plane0_coords = clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);" + )?; + writeln!(self.out, "{l1}if (params.num_planes == 1u) {{")?; + // For single plane, simply sample from plane0 + writeln!( + self.out, + "{l2}return plane0.SampleLevel(samp, plane0_coords, 0.0f);" + )?; + writeln!(self.out, "{l1}}} else {{")?; + + writeln!(self.out, "{l2}float2 plane1_size;")?; + writeln!( + self.out, + "{l2}plane1.GetDimensions(plane1_size.x, plane1_size.y);" + )?; + writeln!( + self.out, + "{l2}float2 plane1_half_texel = float2(0.5, 0.5) / plane1_size;" + )?; + writeln!( + self.out, + "{l2}float2 plane1_coords = clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);" + )?; + + // For multi-plane, sample the Y value from plane 0 + writeln!( + self.out, + "{l2}float y = plane0.SampleLevel(samp, plane0_coords, 0.0f).x;" + )?; + writeln!(self.out, "{l2}float2 uv;")?; + writeln!(self.out, "{l2}if (params.num_planes == 2u) {{")?; + // Sample UV from interleaved plane 1 + writeln!( + self.out, + "{l3}uv = plane1.SampleLevel(samp, plane1_coords, 0.0f).xy;" + )?; + writeln!(self.out, "{l2}}} else {{")?; + // Sample U and V from planes 1 and 2 respectively + writeln!(self.out, "{l3}float2 plane2_size;")?; + writeln!( + self.out, + "{l3}plane2.GetDimensions(plane2_size.x, plane2_size.y);" + )?; + writeln!( + self.out, + "{l3}float2 plane2_half_texel = float2(0.5, 0.5) / plane2_size;" + )?; + writeln!(self.out, "{l3}float2 plane2_coords = clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane2_half_texel);")?; + writeln!(self.out, "{l3}uv = float2(plane1.SampleLevel(samp, plane1_coords, 0.0f).x, plane2.SampleLevel(samp, plane2_coords, 0.0f).x);")?; + writeln!(self.out, "{l2}}}")?; + + self.write_convert_yuv_to_rgb_and_return(l2, "y", "uv", "params")?; + + writeln!(self.out, "{l1}}}")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + WrappedImageSample { + class: + crate::ImageClass::Sampled { + kind: ScalarKind::Float, + multi: false, + }, clamp_to_edge: true, } => { writeln!(self.out, "float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}(Texture2D tex, SamplerState samp, float2 coords) {{")?; @@ -290,6 +571,7 @@ impl super::Writer<'_, W> { crate::ImageClass::Depth { multi: false } => "Depth", crate::ImageClass::Sampled { multi: false, .. } => "", crate::ImageClass::Storage { .. } => "RW", + crate::ImageClass::External => "External", }; let arrayed_str = if query.arrayed { "Array" } else { "" }; let query_str = match query.query { @@ -320,101 +602,133 @@ impl super::Writer<'_, W> { ImageDimension as IDim, }; - const ARGUMENT_VARIABLE_NAME: &str = "tex"; - const RETURN_VARIABLE_NAME: &str = "ret"; - const MIP_LEVEL_PARAM: &str = "mip_level"; - - // Write function return type and name - let ret_ty = func_ctx.resolve_type(expr_handle, &module.types); - self.write_value_type(module, ret_ty)?; - write!(self.out, " ")?; - self.write_wrapped_image_query_function_name(wiq)?; + match wiq.class { + crate::ImageClass::External => { + if wiq.query != ImageQuery::Size { + return Err(super::Error::Custom( + "External images only support `Size` queries".into(), + )); + } - // Write function parameters - write!(self.out, "(")?; - // Texture always first parameter - self.write_image_type(wiq.dim, wiq.arrayed, wiq.class)?; - write!(self.out, " {ARGUMENT_VARIABLE_NAME}")?; - // Mipmap is a second parameter if exists - if let ImageQuery::SizeLevel = wiq.query { - write!(self.out, ", uint {MIP_LEVEL_PARAM}")?; - } - writeln!(self.out, ")")?; + write!(self.out, "uint2 ")?; + self.write_wrapped_image_query_function_name(wiq)?; + let params_name = &self.names + [&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + // Only plane0 and params are used by this implementation, but it's easier to + // always take all of them as arguments so that we can unconditionally expand an + // external texture expression each of its parts. + writeln!(self.out, "(Texture2D plane0, Texture2D plane1, Texture2D plane2, {params_name} params) {{")?; + let l1 = crate::back::Level(1); + let l2 = l1.next(); + writeln!(self.out, "{l1}if (any(params.size)) {{")?; + writeln!(self.out, "{l2}return params.size;")?; + writeln!(self.out, "{l1}}} else {{")?; + // params.size == (0, 0) indicates to query and return plane 0's actual size + writeln!(self.out, "{l2}uint2 ret;")?; + writeln!(self.out, "{l2}plane0.GetDimensions(ret.x, ret.y);")?; + writeln!(self.out, "{l2}return ret;")?; + writeln!(self.out, "{l1}}}")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + _ => { + const ARGUMENT_VARIABLE_NAME: &str = "tex"; + const RETURN_VARIABLE_NAME: &str = "ret"; + const MIP_LEVEL_PARAM: &str = "mip_level"; + + // Write function return type and name + let ret_ty = func_ctx.resolve_type(expr_handle, &module.types); + self.write_value_type(module, ret_ty)?; + write!(self.out, " ")?; + self.write_wrapped_image_query_function_name(wiq)?; + + // Write function parameters + write!(self.out, "(")?; + // Texture always first parameter + self.write_image_type(wiq.dim, wiq.arrayed, wiq.class)?; + write!(self.out, " {ARGUMENT_VARIABLE_NAME}")?; + // Mipmap is a second parameter if exists + if let ImageQuery::SizeLevel = wiq.query { + write!(self.out, ", uint {MIP_LEVEL_PARAM}")?; + } + writeln!(self.out, ")")?; - // Write function body - writeln!(self.out, "{{")?; + // Write function body + writeln!(self.out, "{{")?; - let array_coords = usize::from(wiq.arrayed); - // extra parameter is the mip level count or the sample count - let extra_coords = match wiq.class { - crate::ImageClass::Storage { .. } => 0, - crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => 1, - }; + let array_coords = usize::from(wiq.arrayed); + // extra parameter is the mip level count or the sample count + let extra_coords = match wiq.class { + crate::ImageClass::Storage { .. } => 0, + crate::ImageClass::Sampled { .. } | crate::ImageClass::Depth { .. } => 1, + crate::ImageClass::External => unreachable!(), + }; - // GetDimensions Overloaded Methods - // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions#overloaded-methods - let (ret_swizzle, number_of_params) = match wiq.query { - ImageQuery::Size | ImageQuery::SizeLevel => { - let ret = match wiq.dim { - IDim::D1 => "x", - IDim::D2 => "xy", - IDim::D3 => "xyz", - IDim::Cube => "xy", + // GetDimensions Overloaded Methods + // https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-to-getdimensions#overloaded-methods + let (ret_swizzle, number_of_params) = match wiq.query { + ImageQuery::Size | ImageQuery::SizeLevel => { + let ret = match wiq.dim { + IDim::D1 => "x", + IDim::D2 => "xy", + IDim::D3 => "xyz", + IDim::Cube => "xy", + }; + (ret, ret.len() + array_coords + extra_coords) + } + ImageQuery::NumLevels | ImageQuery::NumSamples | ImageQuery::NumLayers => { + if wiq.arrayed || wiq.dim == IDim::D3 { + ("w", 4) + } else { + ("z", 3) + } + } }; - (ret, ret.len() + array_coords + extra_coords) - } - ImageQuery::NumLevels | ImageQuery::NumSamples | ImageQuery::NumLayers => { - if wiq.arrayed || wiq.dim == IDim::D3 { - ("w", 4) - } else { - ("z", 3) - } - } - }; - // Write `GetDimensions` function. - writeln!(self.out, "{INDENT}uint4 {RETURN_VARIABLE_NAME};")?; - write!(self.out, "{INDENT}{ARGUMENT_VARIABLE_NAME}.GetDimensions(")?; - match wiq.query { - ImageQuery::SizeLevel => { - write!(self.out, "{MIP_LEVEL_PARAM}, ")?; - } - _ => match wiq.class { - crate::ImageClass::Sampled { multi: true, .. } - | crate::ImageClass::Depth { multi: true } - | crate::ImageClass::Storage { .. } => {} - _ => { - // Write zero mipmap level for supported types - write!(self.out, "0, ")?; + // Write `GetDimensions` function. + writeln!(self.out, "{INDENT}uint4 {RETURN_VARIABLE_NAME};")?; + write!(self.out, "{INDENT}{ARGUMENT_VARIABLE_NAME}.GetDimensions(")?; + match wiq.query { + ImageQuery::SizeLevel => { + write!(self.out, "{MIP_LEVEL_PARAM}, ")?; + } + _ => match wiq.class { + crate::ImageClass::Sampled { multi: true, .. } + | crate::ImageClass::Depth { multi: true } + | crate::ImageClass::Storage { .. } => {} + _ => { + // Write zero mipmap level for supported types + write!(self.out, "0, ")?; + } + }, } - }, - } - - for component in COMPONENTS[..number_of_params - 1].iter() { - write!(self.out, "{RETURN_VARIABLE_NAME}.{component}, ")?; - } - // write last parameter without comma and space for last parameter - write!( - self.out, - "{}.{}", - RETURN_VARIABLE_NAME, - COMPONENTS[number_of_params - 1] - )?; + for component in COMPONENTS[..number_of_params - 1].iter() { + write!(self.out, "{RETURN_VARIABLE_NAME}.{component}, ")?; + } - writeln!(self.out, ");")?; + // write last parameter without comma and space for last parameter + write!( + self.out, + "{}.{}", + RETURN_VARIABLE_NAME, + COMPONENTS[number_of_params - 1] + )?; - // Write return value - writeln!( - self.out, - "{INDENT}return {RETURN_VARIABLE_NAME}.{ret_swizzle};" - )?; + writeln!(self.out, ");")?; - // End of function body - writeln!(self.out, "}}")?; - // Write extra new line - writeln!(self.out)?; + // Write return value + writeln!( + self.out, + "{INDENT}return {RETURN_VARIABLE_NAME}.{ret_swizzle};" + )?; + // End of function body + writeln!(self.out, "}}")?; + // Write extra new line + writeln!(self.out)?; + } + } Ok(()) } @@ -1554,10 +1868,31 @@ impl super::Writer<'_, W> { self.write_wrapped_array_length_function(wal)?; } } - crate::Expression::ImageSample { clamp_to_edge, .. } => { - let wrapped = WrappedImageSample { clamp_to_edge }; + crate::Expression::ImageLoad { image, .. } => { + let class = match *func_ctx.resolve_type(image, &module.types) { + crate::TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; + let wrapped = WrappedImageLoad { class }; + if self.wrapped.insert(WrappedType::ImageLoad(wrapped)) { + self.write_wrapped_image_load_function(module, wrapped)?; + } + } + crate::Expression::ImageSample { + image, + clamp_to_edge, + .. + } => { + let class = match *func_ctx.resolve_type(image, &module.types) { + crate::TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; + let wrapped = WrappedImageSample { + class, + clamp_to_edge, + }; if self.wrapped.insert(WrappedType::ImageSample(wrapped)) { - self.write_wrapped_image_sample_function(wrapped)?; + self.write_wrapped_image_sample_function(module, wrapped)?; } } crate::Expression::ImageQuery { image, query } => { diff --git a/naga/src/back/hlsl/keywords.rs b/naga/src/back/hlsl/keywords.rs index f38d47483f7..13f48cef8b5 100644 --- a/naga/src/back/hlsl/keywords.rs +++ b/naga/src/back/hlsl/keywords.rs @@ -1,7 +1,6 @@ +use crate::proc::{CaseInsensitiveKeywordSet, KeywordSet}; use crate::racy_lock::RacyLock; -use hashbrown::HashSet; - // When compiling with FXC without strict mode, these keywords are actually case insensitive. // If you compile with strict mode and specify a different casing like "Pass" instead in an identifier, FXC will give this error: // "error X3086: alternate cases for 'pass' are deprecated in strict mode" @@ -826,6 +825,7 @@ pub const RESERVED: &[&str] = &[ super::writer::INSERT_BITS_FUNCTION, super::writer::SAMPLER_HEAP_VAR, super::writer::COMPARISON_SAMPLER_HEAP_VAR, + super::writer::SAMPLE_EXTERNAL_TEXTURE_FUNCTION, super::writer::ABS_FUNCTION, super::writer::DIV_FUNCTION, super::writer::MOD_FUNCTION, @@ -834,6 +834,7 @@ pub const RESERVED: &[&str] = &[ super::writer::F2U32_FUNCTION, super::writer::F2I64_FUNCTION, super::writer::F2U64_FUNCTION, + super::writer::IMAGE_LOAD_EXTERNAL_FUNCTION, super::writer::IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION, ]; @@ -925,17 +926,11 @@ pub const TYPES: &[&str] = &{ /// significant time during [`Namer::reset`](crate::proc::Namer::reset). /// /// See for benchmarks. -pub static RESERVED_SET: RacyLock> = RacyLock::new(|| { - let mut set = HashSet::default(); - set.reserve(RESERVED.len() + TYPES.len()); - for &word in RESERVED { - set.insert(word); - } - for &word in TYPES { - set.insert(word); - } - set -}); +pub static RESERVED_SET: RacyLock = + RacyLock::new(|| KeywordSet::from_iter(RESERVED.iter().chain(TYPES))); + +pub static RESERVED_CASE_INSENSITIVE_SET: RacyLock = + RacyLock::new(|| CaseInsensitiveKeywordSet::from_iter(RESERVED_CASE_INSENSITIVE)); pub const RESERVED_PREFIXES: &[&str] = &[ "__dynamic_buffer_offsets", diff --git a/naga/src/back/hlsl/mod.rs b/naga/src/back/hlsl/mod.rs index dd9a5bf6e58..c7747eb3fac 100644 --- a/naga/src/back/hlsl/mod.rs +++ b/naga/src/back/hlsl/mod.rs @@ -13,11 +13,17 @@ type should be stored in `uniform` and `storage` buffers. The HLSL we generate must access values in that form, even when it is not what HLSL would use normally. -The rules described here only apply to WGSL `uniform` variables. WGSL -`storage` buffers are translated as HLSL `ByteAddressBuffers`, for -which we generate `Load` and `Store` method calls with explicit byte -offsets. WGSL pipeline inputs must be scalars or vectors; they cannot -be matrices, which is where the interesting problems arise. +Matching the WGSL memory layout is a concern only for `uniform` +variables. WGSL `storage` buffers are translated as HLSL +`ByteAddressBuffers`, for which we generate `Load` and `Store` method +calls with explicit byte offsets. WGSL pipeline inputs must be scalars +or vectors; they cannot be matrices, which is where the interesting +problems arise. However, when an affected type appears in a struct +definition, the transformations described here are applied without +consideration of where the struct is used. + +Access to storage buffers is implemented in `storage.rs`. Access to +uniform buffers is implemented where applicable in `writer.rs`. ## Row- and column-major ordering for matrices @@ -57,10 +63,9 @@ that the columns of a `matKx2` need only be [aligned as required for `vec2`][ilov], which is [eight-byte alignment][8bb]. To compensate for this, any time a `matKx2` appears in a WGSL -`uniform` variable, whether directly as the variable's type or as part -of a struct/array, we actually emit `K` separate `float2` members, and -assemble/disassemble the matrix from its columns (in WGSL; rows in -HLSL) upon load and store. +`uniform` value or as part of a struct/array, we actually emit `K` +separate `float2` members, and assemble/disassemble the matrix from its +columns (in WGSL; rows in HLSL) upon load and store. For example, the following WGSL struct type: @@ -101,6 +106,37 @@ index buffer for each bind group. This buffer is accessed in the shader to get t sampler index within the heap. See the wgpu_hal dx12 backend documentation for more information. +# External textures + +Support for [`crate::ImageClass::External`] textures is implemented by lowering +each external texture global variable to 3 `Texture2D`s, and a `cbuffer` +of type `NagaExternalTextureParams`. This provides up to 3 planes of texture +data (for example single planar RGBA, or separate Y, Cb, and Cr planes), and the +parameters buffer containing information describing how to handle these +correctly. The bind target to use for each of these globals is specified via +[`Options::external_texture_binding_map`]. + +External textures are supported by WGSL's `textureDimensions()`, +`textureLoad()`, and `textureSampleBaseClampToEdge()` built-in functions. These +are implemented using helper functions. See the following functions for how +these are generated: + * `Writer::write_wrapped_image_query_function` + * `Writer::write_wrapped_image_load_function` + * `Writer::write_wrapped_image_sample_function` + +Ideally the set of global variables could be wrapped in a single struct that +could conveniently be passed around. But, alas, HLSL does not allow structs to +have `Texture2D` members. Fortunately, however, external textures can only be +used as arguments to either built-in or user-defined functions. We therefore +expand any external texture function argument to four consecutive arguments (3 +textures and the params struct) when declaring user-defined functions, and +ensure our built-in function implementations take the same arguments. Then, +whenever we need to emit an external texture in `Writer::write_expr`, which +fortunately can only ever be for a global variable or function argument, we +simply emit the variable name of each of the three textures and the parameters +struct in a comma-separated list. This won't win any awards for elegance, but +it works for our purposes. + [hlsl]: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl [ilov]: https://gpuweb.github.io/gpuweb/wgsl/#internal-value-layout [16bb]: https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing#constant-buffer-packing @@ -121,6 +157,35 @@ use thiserror::Error; use crate::{back, ir, proc}; +/// Direct3D 12 binding information for a global variable. +/// +/// This type provides the HLSL-specific information Naga needs to declare and +/// access an HLSL global variable that cannot be derived from the `Module` +/// itself. +/// +/// An HLSL global variable declaration includes details that the Direct3D API +/// will use to refer to it. For example: +/// +/// RWByteAddressBuffer s_sasm : register(u0, space2); +/// +/// This defines a global `s_sasm` that a Direct3D root signature would refer to +/// as register `0` in register space `2` in a `UAV` descriptor range. Naga can +/// infer the register's descriptor range type from the variable's address class +/// (writable [`Storage`] variables are implemented by Direct3D Unordered Access +/// Views, the `u` register type), but the register number and register space +/// must be supplied by the user. +/// +/// The [`back::hlsl::Options`] structure provides `BindTarget`s for various +/// situations in which Naga may need to generate an HLSL global variable, like +/// [`binding_map`] for Naga global variables, or [`push_constants_target`] for +/// a module's sole [`PushConstant`] variable. See those fields' documentation +/// for details. +/// +/// [`Storage`]: crate::ir::AddressSpace::Storage +/// [`back::hlsl::Options`]: Options +/// [`binding_map`]: Options::binding_map +/// [`push_constants_target`]: Options::push_constants_target +/// [`PushConstant`]: crate::ir::AddressSpace::PushConstant #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] @@ -218,7 +283,8 @@ impl crate::ShaderStage { Self::Vertex => "vs", Self::Fragment => "ps", Self::Compute => "cs", - Self::Task | Self::Mesh => unreachable!(), + Self::Task => "as", + Self::Mesh => "ms", } } } @@ -330,6 +396,62 @@ where pub type DynamicStorageBufferOffsetsTargets = alloc::collections::BTreeMap; +/// HLSL binding information for a Naga [`External`] image global variable. +/// +/// See the module documentation's section on [External textures][mod] for details. +/// +/// [`External`]: crate::ir::ImageClass::External +/// [mod]: #external-textures +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +pub struct ExternalTextureBindTarget { + /// HLSL binding information for the individual plane textures. + /// + /// Each of these should refer to an HLSL `Texture2D` holding one + /// plane of data for the external texture. The exact meaning of each plane + /// varies at runtime depending on where the external texture's data + /// originated. + pub planes: [BindTarget; 3], + + /// HLSL binding information for a buffer holding the sampling parameters. + /// + /// This should refer to a cbuffer of type `NagaExternalTextureParams`, that + /// the code Naga generates for `textureSampleBaseClampToEdge` consults to + /// decide how to combine the data in [`planes`] to get the result required + /// by the spec. + /// + /// [`planes`]: Self::planes + pub params: BindTarget, +} + +#[cfg(any(feature = "serialize", feature = "deserialize"))] +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +struct ExternalTextureBindingMapSerialization { + resource_binding: crate::ResourceBinding, + bind_target: ExternalTextureBindTarget, +} + +#[cfg(feature = "deserialize")] +fn deserialize_external_texture_binding_map<'de, D>( + deserializer: D, +) -> Result +where + D: serde::Deserializer<'de>, +{ + use serde::Deserialize; + + let vec = Vec::::deserialize(deserializer)?; + let mut map = ExternalTextureBindingMap::default(); + for item in vec { + map.insert(item.resource_binding, item.bind_target); + } + Ok(map) +} +pub type ExternalTextureBindingMap = + alloc::collections::BTreeMap; + /// Shorthand result used internally by the backend type BackendResult = Result<(), Error>; @@ -349,21 +471,47 @@ pub enum EntryPointError { pub struct Options { /// The hlsl shader model to be used pub shader_model: ShaderModel, - /// Map of resources association to binding locations. + + /// HLSL binding information for each Naga global variable. + /// + /// This maps Naga [`GlobalVariable`]'s [`ResourceBinding`]s to a + /// [`BindTarget`] specifying its register number and space, along with + /// other details necessary to generate a full HLSL declaration for it, + /// or to access its value. + /// + /// This must provide a [`BindTarget`] for every [`GlobalVariable`] in the + /// [`Module`] that has a [`binding`]. + /// + /// [`GlobalVariable`]: crate::ir::GlobalVariable + /// [`ResourceBinding`]: crate::ir::ResourceBinding + /// [`Module`]: crate::ir::Module + /// [`binding`]: crate::ir::GlobalVariable::binding #[cfg_attr( feature = "deserialize", serde(deserialize_with = "deserialize_binding_map") )] pub binding_map: BindingMap, + /// Don't panic on missing bindings, instead generate any HLSL. pub fake_missing_bindings: bool, /// Add special constants to `SV_VertexIndex` and `SV_InstanceIndex`, /// to make them work like in Vulkan/Metal, with help of the host. pub special_constants_binding: Option, - /// Bind target of the push constant buffer + + /// HLSL binding information for the [`PushConstant`] global, if present. + /// + /// If a module contains a global in the [`PushConstant`] address space, the + /// `dx12` backend stores its value directly in the root signature as a + /// series of [`D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS`], whose binding + /// information is given here. + /// + /// [`PushConstant`]: crate::ir::AddressSpace::PushConstant + /// [`D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS`]: https://learn.microsoft.com/en-us/windows/win32/api/d3d12/ne-d3d12-d3d12_root_parameter_type pub push_constants_target: Option, - /// Bind target of the sampler heap and comparison sampler heap. + + /// HLSL binding information for the sampler heap and comparison sampler heap. pub sampler_heap_target: SamplerHeapBindTargets, + /// Mapping of each bind group's sampler index buffer to a bind target. #[cfg_attr( feature = "deserialize", @@ -376,6 +524,18 @@ pub struct Options { serde(deserialize_with = "deserialize_storage_buffer_offsets") )] pub dynamic_storage_buffer_offsets_targets: DynamicStorageBufferOffsetsTargets, + #[cfg_attr( + feature = "deserialize", + serde(deserialize_with = "deserialize_external_texture_binding_map") + )] + + /// HLSL binding information for [`External`] image global variables. + /// + /// See [`ExternalTextureBindTarget`] for details. + /// + /// [`External`]: crate::ir::ImageClass::External + pub external_texture_binding_map: ExternalTextureBindingMap, + /// Should workgroup variables be zero initialized (by polyfilling)? pub zero_initialize_workgroup_memory: bool, /// Should we restrict indexing of vectors, matrices and arrays? @@ -396,6 +556,7 @@ impl Default for Options { sampler_buffer_binding_map: alloc::collections::BTreeMap::default(), push_constants_target: None, dynamic_storage_buffer_offsets_targets: alloc::collections::BTreeMap::new(), + external_texture_binding_map: ExternalTextureBindingMap::default(), zero_initialize_workgroup_memory: true, restrict_indexing: true, force_loop_bounding: true, @@ -420,6 +581,29 @@ impl Options { None => Err(EntryPointError::MissingBinding(*res_binding)), } } + + fn resolve_external_texture_resource_binding( + &self, + res_binding: &crate::ResourceBinding, + ) -> Result { + match self.external_texture_binding_map.get(res_binding) { + Some(target) => Ok(*target), + None if self.fake_missing_bindings => { + let fake = BindTarget { + space: res_binding.group as u8, + register: res_binding.binding, + binding_array_size: None, + dynamic_storage_buffer_offsets_index: None, + restrict_indexing: false, + }; + Ok(ExternalTextureBindTarget { + planes: [fake, fake, fake], + params: fake, + }) + } + None => Err(EntryPointError::MissingBinding(*res_binding)), + } + } } /// Reflection info for entry point names. @@ -474,6 +658,7 @@ enum WrappedType { ArrayLength(help::WrappedArrayLength), ImageSample(help::WrappedImageSample), ImageQuery(help::WrappedImageQuery), + ImageLoad(help::WrappedImageLoad), ImageLoadScalar(crate::Scalar), Constructor(help::WrappedConstructor), StructMatrixAccess(help::WrappedStructMatrixAccess), diff --git a/naga/src/back/hlsl/storage.rs b/naga/src/back/hlsl/storage.rs index 9f92d866398..5d66af00087 100644 --- a/naga/src/back/hlsl/storage.rs +++ b/naga/src/back/hlsl/storage.rs @@ -108,6 +108,13 @@ pub(super) enum StoreValue { base: Handle, member_index: u32, }, + // Access to a single column of a Cx2 matrix within a struct + TempColumnAccess { + depth: usize, + base: Handle, + member_index: u32, + column: u32, + }, } impl super::Writer<'_, W> { @@ -290,6 +297,15 @@ impl super::Writer<'_, W> { let name = &self.names[&NameKey::StructMember(base, member_index)]; write!(self.out, "{STORE_TEMP_NAME}{depth}.{name}")? } + StoreValue::TempColumnAccess { + depth, + base, + member_index, + column, + } => { + let name = &self.names[&NameKey::StructMember(base, member_index)]; + write!(self.out, "{STORE_TEMP_NAME}{depth}.{name}_{column}")? + } } Ok(()) } @@ -302,6 +318,7 @@ impl super::Writer<'_, W> { value: StoreValue, func_ctx: &FunctionCtx, level: crate::back::Level, + within_struct: Option>, ) -> BackendResult { let temp_resolution; let ty_resolution = match value { @@ -325,6 +342,9 @@ impl super::Writer<'_, W> { temp_resolution = TypeResolution::Handle(ty_handle); &temp_resolution } + StoreValue::TempColumnAccess { .. } => { + unreachable!("attempting write_storage_store for TempColumnAccess"); + } }; match *ty_resolution.inner_with(&module.types) { crate::TypeInner::Scalar(scalar) => { @@ -372,37 +392,92 @@ impl super::Writer<'_, W> { rows, scalar, } => { - // first, assign the value to a temporary - writeln!(self.out, "{level}{{")?; - let depth = level.0 + 1; - write!( - self.out, - "{}{}{}x{} {}{} = ", - level.next(), - scalar.to_hlsl_str()?, - columns as u8, - rows as u8, - STORE_TEMP_NAME, - depth, - )?; - self.write_store_value(module, &value, func_ctx)?; - writeln!(self.out, ";")?; - // Note: Matrices containing vec3s, due to padding, act like they contain vec4s. let row_stride = Alignment::from(rows) * scalar.width as u32; - // then iterate the stores - for i in 0..columns as u32 { - self.temp_access_chain - .push(SubAccess::Offset(i * row_stride)); - let ty_inner = crate::TypeInner::Vector { size: rows, scalar }; - let sv = StoreValue::TempIndex { - depth, - index: i, - ty: TypeResolution::Value(ty_inner), - }; - self.write_storage_store(module, var_handle, sv, func_ctx, level.next())?; - self.temp_access_chain.pop(); + writeln!(self.out, "{level}{{")?; + + match within_struct { + Some(containing_struct) if rows == crate::VectorSize::Bi => { + // If we are within a struct, then the struct was already assigned to + // a temporary, we don't need to make another. + let mut chain = mem::take(&mut self.temp_access_chain); + for i in 0..columns as u32 { + chain.push(SubAccess::Offset(i * row_stride)); + // working around the borrow checker in `self.write_expr` + let var_name = &self.names[&NameKey::GlobalVariable(var_handle)]; + let StoreValue::TempAccess { member_index, .. } = value else { + unreachable!( + "write_storage_store within_struct but not TempAccess" + ); + }; + let column_value = StoreValue::TempColumnAccess { + depth: level.0, // note not incrementing, b/c no temp + base: containing_struct, + member_index, + column: i, + }; + // See note about DXC and Load/Store in the module's documentation. + if scalar.width == 4 { + write!( + self.out, + "{}{}.Store{}(", + level.next(), + var_name, + rows as u8 + )?; + self.write_storage_address(module, &chain, func_ctx)?; + write!(self.out, ", asuint(")?; + self.write_store_value(module, &column_value, func_ctx)?; + writeln!(self.out, "));")?; + } else { + write!(self.out, "{}{var_name}.Store(", level.next())?; + self.write_storage_address(module, &chain, func_ctx)?; + write!(self.out, ", ")?; + self.write_store_value(module, &column_value, func_ctx)?; + writeln!(self.out, ");")?; + } + chain.pop(); + } + self.temp_access_chain = chain; + } + _ => { + // first, assign the value to a temporary + let depth = level.0 + 1; + write!( + self.out, + "{}{}{}x{} {}{} = ", + level.next(), + scalar.to_hlsl_str()?, + columns as u8, + rows as u8, + STORE_TEMP_NAME, + depth, + )?; + self.write_store_value(module, &value, func_ctx)?; + writeln!(self.out, ";")?; + + // then iterate the stores + for i in 0..columns as u32 { + self.temp_access_chain + .push(SubAccess::Offset(i * row_stride)); + let ty_inner = crate::TypeInner::Vector { size: rows, scalar }; + let sv = StoreValue::TempIndex { + depth, + index: i, + ty: TypeResolution::Value(ty_inner), + }; + self.write_storage_store( + module, + var_handle, + sv, + func_ctx, + level.next(), + None, + )?; + self.temp_access_chain.pop(); + } + } } // done writeln!(self.out, "{level}}}")?; @@ -415,7 +490,7 @@ impl super::Writer<'_, W> { // first, assign the value to a temporary writeln!(self.out, "{level}{{")?; write!(self.out, "{}", level.next())?; - self.write_value_type(module, &module.types[base].inner)?; + self.write_type(module, base)?; let depth = level.next().0; write!(self.out, " {STORE_TEMP_NAME}{depth}")?; self.write_array_size(module, base, crate::ArraySize::Constant(size))?; @@ -430,7 +505,7 @@ impl super::Writer<'_, W> { index: i, ty: TypeResolution::Handle(base), }; - self.write_storage_store(module, var_handle, sv, func_ctx, level.next())?; + self.write_storage_store(module, var_handle, sv, func_ctx, level.next(), None)?; self.temp_access_chain.pop(); } // done @@ -461,7 +536,14 @@ impl super::Writer<'_, W> { base: struct_ty, member_index: i as u32, }; - self.write_storage_store(module, var_handle, sv, func_ctx, level.next())?; + self.write_storage_store( + module, + var_handle, + sv, + func_ctx, + level.next(), + Some(struct_ty), + )?; self.temp_access_chain.pop(); } // done diff --git a/naga/src/back/hlsl/writer.rs b/naga/src/back/hlsl/writer.rs index 2096aff0f97..ab95b9327f9 100644 --- a/naga/src/back/hlsl/writer.rs +++ b/naga/src/back/hlsl/writer.rs @@ -17,7 +17,7 @@ use super::{ use crate::{ back::{self, get_entry_points, Baked}, common, - proc::{self, index, NameKey}, + proc::{self, index, ExternalTextureNameKey, NameKey}, valid, Handle, Module, RayQueryFunction, Scalar, ScalarKind, ShaderStage, TypeInner, }; @@ -34,6 +34,7 @@ pub(crate) const EXTRACT_BITS_FUNCTION: &str = "naga_extractBits"; pub(crate) const INSERT_BITS_FUNCTION: &str = "naga_insertBits"; pub(crate) const SAMPLER_HEAP_VAR: &str = "nagaSamplerHeap"; pub(crate) const COMPARISON_SAMPLER_HEAP_VAR: &str = "nagaComparisonSamplerHeap"; +pub(crate) const SAMPLE_EXTERNAL_TEXTURE_FUNCTION: &str = "nagaSampleExternalTexture"; pub(crate) const ABS_FUNCTION: &str = "naga_abs"; pub(crate) const DIV_FUNCTION: &str = "naga_div"; pub(crate) const MOD_FUNCTION: &str = "naga_mod"; @@ -44,6 +45,12 @@ pub(crate) const F2I64_FUNCTION: &str = "naga_f2i64"; pub(crate) const F2U64_FUNCTION: &str = "naga_f2u64"; pub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str = "nagaTextureSampleBaseClampToEdge"; +pub(crate) const IMAGE_LOAD_EXTERNAL_FUNCTION: &str = "nagaTextureLoadExternal"; + +enum Index { + Expression(Handle), + Static(u32), +} struct EpStructMember { name: String, @@ -148,7 +155,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { self.namer.reset( module, &super::keywords::RESERVED_SET, - super::keywords::RESERVED_CASE_INSENSITIVE, + &super::keywords::RESERVED_CASE_INSENSITIVE_SET, super::keywords::RESERVED_PREFIXES, &mut self.names, ); @@ -312,7 +319,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } for (group, bt) in self.options.dynamic_storage_buffer_offsets_targets.iter() { - writeln!(self.out, "struct __dynamic_buffer_offsetsTy{} {{", group)?; + writeln!(self.out, "struct __dynamic_buffer_offsetsTy{group} {{")?; for i in 0..bt.size { writeln!(self.out, "{}uint _{};", back::INDENT, i)?; } @@ -387,8 +394,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } // Write all globals - for (ty, _) in module.global_variables.iter() { - self.write_global(module, ty)?; + for (global, _) in module.global_variables.iter() { + self.write_global(module, global)?; } if !module.global_variables.is_empty() { @@ -426,6 +433,10 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { .find(|&(var_handle, var)| match var.binding { Some(ref binding) if !info[var_handle].is_empty() => { self.options.resolve_resource_binding(binding).is_err() + && self + .options + .resolve_external_texture_resource_binding(binding) + .is_err() } _ => false, }) @@ -468,8 +479,14 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { match var.binding { Some(ref binding) if !info[var_handle].is_empty() => { if let Err(err) = self.options.resolve_resource_binding(binding) { - ep_error = Some(err); - break; + if self + .options + .resolve_external_texture_resource_binding(binding) + .is_err() + { + ep_error = Some(err); + break; + } } } _ => {} @@ -701,7 +718,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { let members = match module.types[result.ty].inner { TypeInner::Struct { ref members, .. } => members, ref other => { - log::error!("Unexpected {:?} output type without a binding", other); + log::error!("Unexpected {other:?} output type without a binding"); &empty[..] } }; @@ -899,6 +916,25 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { let global = &module.global_variables[handle]; let inner = &module.types[global.ty].inner; + let handle_ty = match *inner { + TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner, + _ => inner, + }; + + // External textures are handled entirely differently, so defer entirely to that method. + // We do so prior to calling resolve_resource_binding() below, as we even need to resolve + // their bindings separately. + let is_external_texture = matches!( + *handle_ty, + TypeInner::Image { + class: crate::ImageClass::External, + .. + } + ); + if is_external_texture { + return self.write_global_external_texture(module, handle, global); + } + if let Some(ref binding) = global.binding { if let Err(err) = self.options.resolve_resource_binding(binding) { log::info!( @@ -911,11 +947,6 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } } - let handle_ty = match *inner { - TypeInner::BindingArray { ref base, .. } => &module.types[*base].inner, - _ => inner, - }; - // Samplers are handled entirely differently, so defer entirely to that method. let is_sampler = matches!(*handle_ty, TypeInner::Sampler { .. }); @@ -1128,6 +1159,70 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { Ok(()) } + /// Write the declarations for an external texture global variable. + /// These are emitted as multiple global variables: Three `Texture2D`s + /// (one for each plane) and a parameters cbuffer. + fn write_global_external_texture( + &mut self, + module: &Module, + handle: Handle, + global: &crate::GlobalVariable, + ) -> BackendResult { + let res_binding = global + .binding + .as_ref() + .expect("External texture global variables must have a resource binding"); + let ext_tex_bindings = match self + .options + .resolve_external_texture_resource_binding(res_binding) + { + Ok(bindings) => bindings, + Err(err) => { + log::info!( + "Skipping global {:?} (name {:?}) for being inaccessible: {}", + handle, + global.name, + err, + ); + return Ok(()); + } + }; + + let mut write_plane = |bt: &super::BindTarget, name| -> BackendResult { + write!( + self.out, + "Texture2D {}: register(t{}", + name, bt.register + )?; + if bt.space != 0 { + write!(self.out, ", space{}", bt.space)?; + } + writeln!(self.out, ");")?; + Ok(()) + }; + for (i, bt) in ext_tex_bindings.planes.iter().enumerate() { + let plane_name = &self.names + [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Plane(i))]; + write_plane(bt, plane_name)?; + } + + let params_name = &self.names + [&NameKey::ExternalTextureGlobalVariable(handle, ExternalTextureNameKey::Params)]; + let params_ty_name = + &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + write!( + self.out, + "cbuffer {}: register(b{}", + params_name, ext_tex_bindings.params.register + )?; + if ext_tex_bindings.params.space != 0 { + write!(self.out, ", space{}", ext_tex_bindings.params.space)?; + } + writeln!(self.out, ") {{ {params_ty_name} {params_name}; }};")?; + + Ok(()) + } + /// Helper method used to write global constants /// /// # Notes @@ -1428,7 +1523,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { let array_return_type = self.namer.call(&format!("ret_{name}")); write!(self.out, "typedef ")?; self.write_type(module, result.ty)?; - write!(self.out, " {}", array_return_type)?; + write!(self.out, " {array_return_type}")?; self.write_array_size(module, base, size)?; writeln!(self.out, ";")?; Some(array_return_type) @@ -1480,26 +1575,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { if index != 0 { write!(self.out, ", ")?; } - // Write argument type - let arg_ty = match module.types[arg.ty].inner { - // pointers in function arguments are expected and resolve to `inout` - TypeInner::Pointer { base, .. } => { - //TODO: can we narrow this down to just `in` when possible? - write!(self.out, "inout ")?; - base - } - _ => arg.ty, - }; - self.write_type(module, arg_ty)?; - - let argument_name = - &self.names[&NameKey::FunctionArgument(handle, index as u32)]; - // Write argument name. Space is important. - write!(self.out, " {argument_name}")?; - if let TypeInner::Array { base, size, .. } = module.types[arg_ty].inner { - self.write_array_size(module, base, size)?; - } + self.write_function_argument(module, handle, arg, index)?; } } back::FunctionType::EntryPoint(ep_index) => { @@ -1613,6 +1690,74 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { Ok(()) } + fn write_function_argument( + &mut self, + module: &Module, + handle: Handle, + arg: &crate::FunctionArgument, + index: usize, + ) -> BackendResult { + // External texture arguments must be expanded into separate + // arguments for each plane and the params buffer. + if let TypeInner::Image { + class: crate::ImageClass::External, + .. + } = module.types[arg.ty].inner + { + return self.write_function_external_texture_argument(module, handle, index); + } + + // Write argument type + let arg_ty = match module.types[arg.ty].inner { + // pointers in function arguments are expected and resolve to `inout` + TypeInner::Pointer { base, .. } => { + //TODO: can we narrow this down to just `in` when possible? + write!(self.out, "inout ")?; + base + } + _ => arg.ty, + }; + self.write_type(module, arg_ty)?; + + let argument_name = &self.names[&NameKey::FunctionArgument(handle, index as u32)]; + + // Write argument name. Space is important. + write!(self.out, " {argument_name}")?; + if let TypeInner::Array { base, size, .. } = module.types[arg_ty].inner { + self.write_array_size(module, base, size)?; + } + + Ok(()) + } + + fn write_function_external_texture_argument( + &mut self, + module: &Module, + handle: Handle, + index: usize, + ) -> BackendResult { + let plane_names = [0, 1, 2].map(|i| { + &self.names[&NameKey::ExternalTextureFunctionArgument( + handle, + index as u32, + ExternalTextureNameKey::Plane(i), + )] + }); + let params_name = &self.names[&NameKey::ExternalTextureFunctionArgument( + handle, + index as u32, + ExternalTextureNameKey::Params, + )]; + let params_ty_name = + &self.names[&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + write!( + self.out, + "Texture2D {}, Texture2D {}, Texture2D {}, {params_ty_name} {params_name}", + plane_names[0], plane_names[1], plane_names[2], + )?; + Ok(()) + } + fn need_workgroup_variables_initialization( &mut self, func_ctx: &back::FunctionCtx, @@ -1797,6 +1942,23 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { Ok(()) } + fn write_index( + &mut self, + module: &Module, + index: Index, + func_ctx: &back::FunctionCtx<'_>, + ) -> BackendResult { + match index { + Index::Static(index) => { + write!(self.out, "{index}")?; + } + Index::Expression(index) => { + self.write_expr(module, index, func_ctx)?; + } + } + Ok(()) + } + /// Helper method used to write statements /// /// # Notes @@ -1945,6 +2107,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { StoreValue::Expression(value), func_ctx, level, + None, )?; } else { // We treat matrices of the form `matCx2` as a sequence of C `vec2`s. @@ -1952,13 +2115,15 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { // // We handle matrix Stores here directly (including sub accesses for Vectors and Scalars). // Loads are handled by `Expression::AccessIndex` (since sub accesses work fine for Loads). - struct MatrixAccess { - base: Handle, - index: u32, - } - enum Index { - Expression(Handle), - Static(u32), + enum MatrixAccess { + Direct { + base: Handle, + index: u32, + }, + Struct { + columns: crate::VectorSize, + base: Handle, + }, } let get_members = |expr: Handle| { @@ -1972,187 +2137,28 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } }; - let mut matrix = None; - let mut vector = None; - let mut scalar = None; - - let mut current_expr = pointer; - for _ in 0..3 { - let resolved = func_ctx.resolve_type(current_expr, &module.types); - - match (resolved, &func_ctx.expressions[current_expr]) { - ( - &TypeInner::Pointer { base: ty, .. }, - &crate::Expression::AccessIndex { base, index }, - ) if matches!( - module.types[ty].inner, - TypeInner::Matrix { - rows: crate::VectorSize::Bi, - .. - } - ) && get_members(base) - .map(|members| members[index as usize].binding.is_none()) - == Some(true) => - { - matrix = Some(MatrixAccess { base, index }); - break; - } - ( - &TypeInner::ValuePointer { - size: Some(crate::VectorSize::Bi), - .. - }, - &crate::Expression::Access { base, index }, - ) => { - vector = Some(Index::Expression(index)); - current_expr = base; - } - ( - &TypeInner::ValuePointer { - size: Some(crate::VectorSize::Bi), - .. - }, - &crate::Expression::AccessIndex { base, index }, - ) => { - vector = Some(Index::Static(index)); - current_expr = base; - } - ( - &TypeInner::ValuePointer { size: None, .. }, - &crate::Expression::Access { base, index }, - ) => { - scalar = Some(Index::Expression(index)); - current_expr = base; - } - ( - &TypeInner::ValuePointer { size: None, .. }, - &crate::Expression::AccessIndex { base, index }, - ) => { - scalar = Some(Index::Static(index)); - current_expr = base; - } - _ => break, - } - } - write!(self.out, "{level}")?; - if let Some(MatrixAccess { index, base }) = matrix { - let base_ty_res = &func_ctx.info[base].ty; - let resolved = base_ty_res.inner_with(&module.types); - let ty = match *resolved { - TypeInner::Pointer { base, .. } => base, - _ => base_ty_res.handle().unwrap(), - }; - - if let Some(Index::Static(vec_index)) = vector { - self.write_expr(module, base, func_ctx)?; - write!( - self.out, - ".{}_{}", - &self.names[&NameKey::StructMember(ty, index)], - vec_index - )?; - - if let Some(scalar_index) = scalar { - write!(self.out, "[")?; - match scalar_index { - Index::Static(index) => { - write!(self.out, "{index}")?; - } - Index::Expression(index) => { - self.write_expr(module, index, func_ctx)?; - } - } - write!(self.out, "]")?; - } - - write!(self.out, " = ")?; - self.write_expr(module, value, func_ctx)?; - writeln!(self.out, ";")?; - } else { - let access = WrappedStructMatrixAccess { ty, index }; - match (&vector, &scalar) { - (&Some(_), &Some(_)) => { - self.write_wrapped_struct_matrix_set_scalar_function_name( - access, - )?; - } - (&Some(_), &None) => { - self.write_wrapped_struct_matrix_set_vec_function_name(access)?; - } - (&None, _) => { - self.write_wrapped_struct_matrix_set_function_name(access)?; - } - } - - write!(self.out, "(")?; - self.write_expr(module, base, func_ctx)?; - write!(self.out, ", ")?; - self.write_expr(module, value, func_ctx)?; - - if let Some(Index::Expression(vec_index)) = vector { - write!(self.out, ", ")?; - self.write_expr(module, vec_index, func_ctx)?; - - if let Some(scalar_index) = scalar { - write!(self.out, ", ")?; - match scalar_index { - Index::Static(index) => { - write!(self.out, "{index}")?; - } - Index::Expression(index) => { - self.write_expr(module, index, func_ctx)?; - } - } - } - } - writeln!(self.out, ");")?; - } - } else { - // We handle `Store`s to __matCx2 column vectors and scalar elements via - // the previously injected functions __set_col_of_matCx2 / __set_el_of_matCx2. - struct MatrixData { - columns: crate::VectorSize, - base: Handle, - } - - enum Index { - Expression(Handle), - Static(u32), - } - - let mut matrix = None; - let mut vector = None; - let mut scalar = None; - - let mut current_expr = pointer; - for _ in 0..3 { - let resolved = func_ctx.resolve_type(current_expr, &module.types); - match (resolved, &func_ctx.expressions[current_expr]) { - ( - &TypeInner::ValuePointer { - size: Some(crate::VectorSize::Bi), - .. - }, - &crate::Expression::Access { base, index }, - ) => { - vector = Some(index); - current_expr = base; - } - ( - &TypeInner::ValuePointer { size: None, .. }, - &crate::Expression::Access { base, index }, - ) => { - scalar = Some(Index::Expression(index)); - current_expr = base; - } + let matrix_access_on_lhs = + find_matrix_in_access_chain(module, pointer, func_ctx).and_then( + |(matrix_expr, vector, scalar)| match ( + func_ctx.resolve_type(matrix_expr, &module.types), + &func_ctx.expressions[matrix_expr], + ) { ( - &TypeInner::ValuePointer { size: None, .. }, + &TypeInner::Pointer { base: ty, .. }, &crate::Expression::AccessIndex { base, index }, - ) => { - scalar = Some(Index::Static(index)); - current_expr = base; + ) if matches!( + module.types[ty].inner, + TypeInner::Matrix { + rows: crate::VectorSize::Bi, + .. + } + ) && get_members(base) + .map(|members| members[index as usize].binding.is_none()) + == Some(true) => + { + Some((MatrixAccess::Direct { base, index }, vector, scalar)) } _ => { if let Some(MatrixType { @@ -2161,24 +2167,95 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { width: 4, }) = get_inner_matrix_of_struct_array_member( module, - current_expr, + matrix_expr, func_ctx, true, ) { - matrix = Some(MatrixData { - columns, - base: current_expr, - }); + Some(( + MatrixAccess::Struct { + columns, + base: matrix_expr, + }, + vector, + scalar, + )) + } else { + None } + } + }, + ); + + match matrix_access_on_lhs { + Some((MatrixAccess::Direct { index, base }, vector, scalar)) => { + let base_ty_res = &func_ctx.info[base].ty; + let resolved = base_ty_res.inner_with(&module.types); + let ty = match *resolved { + TypeInner::Pointer { base, .. } => base, + _ => base_ty_res.handle().unwrap(), + }; - break; + if let Some(Index::Static(vec_index)) = vector { + self.write_expr(module, base, func_ctx)?; + write!( + self.out, + ".{}_{}", + &self.names[&NameKey::StructMember(ty, index)], + vec_index + )?; + + if let Some(scalar_index) = scalar { + write!(self.out, "[")?; + self.write_index(module, scalar_index, func_ctx)?; + write!(self.out, "]")?; + } + + write!(self.out, " = ")?; + self.write_expr(module, value, func_ctx)?; + writeln!(self.out, ";")?; + } else { + let access = WrappedStructMatrixAccess { ty, index }; + match (&vector, &scalar) { + (&Some(_), &Some(_)) => { + self.write_wrapped_struct_matrix_set_scalar_function_name( + access, + )?; + } + (&Some(_), &None) => { + self.write_wrapped_struct_matrix_set_vec_function_name( + access, + )?; + } + (&None, _) => { + self.write_wrapped_struct_matrix_set_function_name(access)?; + } + } + + write!(self.out, "(")?; + self.write_expr(module, base, func_ctx)?; + write!(self.out, ", ")?; + self.write_expr(module, value, func_ctx)?; + + if let Some(Index::Expression(vec_index)) = vector { + write!(self.out, ", ")?; + self.write_expr(module, vec_index, func_ctx)?; + + if let Some(scalar_index) = scalar { + write!(self.out, ", ")?; + self.write_index(module, scalar_index, func_ctx)?; + } } + writeln!(self.out, ");")?; } } + Some(( + MatrixAccess::Struct { columns, base }, + Some(Index::Expression(vec_index)), + scalar, + )) => { + // We handle `Store`s to __matCx2 column vectors and scalar elements via + // the previously injected functions __set_col_of_matCx2 / __set_el_of_matCx2. - if let (Some(MatrixData { columns, base }), Some(vec_index)) = - (matrix, vector) - { if scalar.is_some() { write!(self.out, "__set_el_of_mat{}x2", columns as u8)?; } else { @@ -2191,21 +2268,17 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { if let Some(scalar_index) = scalar { write!(self.out, ", ")?; - match scalar_index { - Index::Static(index) => { - write!(self.out, "{index}")?; - } - Index::Expression(index) => { - self.write_expr(module, index, func_ctx)?; - } - } + self.write_index(module, scalar_index, func_ctx)?; } write!(self.out, ", ")?; self.write_expr(module, value, func_ctx)?; writeln!(self.out, ");")?; - } else { + } + Some((MatrixAccess::Struct { .. }, Some(Index::Static(_)), _)) + | Some((MatrixAccess::Struct { .. }, None, _)) + | None => { self.write_expr(module, pointer, func_ctx)?; write!(self.out, " = ")?; @@ -2963,6 +3036,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { rows: crate::VectorSize::Bi, width: 4, }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true) + .or_else(|| get_global_uniform_matrix(module, base, func_ctx)) { write!(self.out, "__get_col_of_mat{}x2(", columns as u8)?; self.write_expr(module, base, func_ctx)?; @@ -3075,13 +3149,15 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { { // do nothing, the chain is written on `Load`/`Store` } else { - // We write the matrix column access in a special way since - // the type of `base` is our special __matCx2 struct. + // See if we need to write the matrix column access in a + // special way since the type of `base` is our special + // __matCx2 struct. if let Some(MatrixType { rows: crate::VectorSize::Bi, width: 4, .. }) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true) + .or_else(|| get_global_uniform_matrix(module, base, func_ctx)) { self.write_expr(module, base, func_ctx)?; write!(self.out, "._{index}")?; @@ -3181,9 +3257,34 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { } } Expression::FunctionArgument(pos) => { - let key = func_ctx.argument_key(pos); - let name = &self.names[&key]; - write!(self.out, "{name}")?; + let ty = func_ctx.resolve_type(expr, &module.types); + + // We know that any external texture function argument has been expanded into + // separate consecutive arguments for each plane and the parameters buffer. And we + // also know that external textures can only ever be used as an argument to another + // function. Therefore we can simply emit each of the expanded arguments in a + // consecutive comma-separated list. + if let TypeInner::Image { + class: crate::ImageClass::External, + .. + } = *ty + { + let plane_names = [0, 1, 2].map(|i| { + &self.names[&func_ctx + .external_texture_argument_key(pos, ExternalTextureNameKey::Plane(i))] + }); + let params_name = &self.names[&func_ctx + .external_texture_argument_key(pos, ExternalTextureNameKey::Params)]; + write!( + self.out, + "{}, {}, {}, {}", + plane_names[0], plane_names[1], plane_names[2], params_name + )?; + } else { + let key = func_ctx.argument_key(pos); + let name = &self.names[&key]; + write!(self.out, "{name}")?; + } } Expression::ImageSample { coordinate, @@ -3346,7 +3447,34 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { let is_storage_space = matches!(global_variable.space, crate::AddressSpace::Storage { .. }); - if !is_binding_array_of_samplers && !is_storage_space { + // Our external texture global variable has been expanded into multiple + // global variables, one for each plane and the parameters buffer. + // External textures can only ever be used as arguments to a function + // call, and we know that an external texture argument to any function + // will have been expanded to separate consecutive arguments for each + // plane and the parameters buffer. Therefore we can simply emit each of + // the expanded global variables in a consecutive comma-separated list. + if let TypeInner::Image { + class: crate::ImageClass::External, + .. + } = *ty + { + let plane_names = [0, 1, 2].map(|i| { + &self.names[&NameKey::ExternalTextureGlobalVariable( + handle, + ExternalTextureNameKey::Plane(i), + )] + }); + let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable( + handle, + ExternalTextureNameKey::Params, + )]; + write!( + self.out, + "{}, {}, {}, {}", + plane_names[0], plane_names[1], plane_names[2], params_name + )?; + } else if !is_binding_array_of_samplers && !is_storage_space { let name = &self.names[&NameKey::GlobalVariable(handle)]; write!(self.out, "{name}")?; } @@ -3381,8 +3509,11 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { .or_else(|| get_inner_matrix_of_global_uniform(module, pointer, func_ctx)) { let mut resolved = func_ctx.resolve_type(pointer, &module.types); - if let TypeInner::Pointer { base, .. } = *resolved { - resolved = &module.types[base].inner; + let ptr_tr = resolved.pointer_base_type(); + if let Some(ptr_ty) = + ptr_tr.as_ref().map(|tr| tr.inner_with(&module.types)) + { + resolved = ptr_ty; } write!(self.out, "((")?; @@ -4174,6 +4305,17 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> { ) -> Result<(), Error> { let mut wrapping_type = None; match *func_ctx.resolve_type(image, &module.types) { + TypeInner::Image { + class: crate::ImageClass::External, + .. + } => { + write!(self.out, "{IMAGE_LOAD_EXTERNAL_FUNCTION}(")?; + self.write_expr(module, image, func_ctx)?; + write!(self.out, ", ")?; + self.write_expr(module, coordinate, func_ctx)?; + write!(self.out, ")")?; + return Ok(()); + } TypeInner::Image { class: crate::ImageClass::Storage { format, .. }, .. @@ -4416,6 +4558,44 @@ pub(super) fn get_inner_matrix_data( } } +/// If `base` is an access chain of the form `mat`, `mat[col]`, or `mat[col][row]`, +/// returns a tuple of the matrix, the column (vector) index (if present), and +/// the row (scalar) index (if present). +fn find_matrix_in_access_chain( + module: &Module, + base: Handle, + func_ctx: &back::FunctionCtx<'_>, +) -> Option<(Handle, Option, Option)> { + let mut current_base = base; + let mut vector = None; + let mut scalar = None; + loop { + let resolved_tr = func_ctx + .resolve_type(current_base, &module.types) + .pointer_base_type(); + let resolved = resolved_tr.as_ref()?.inner_with(&module.types); + + match *resolved { + TypeInner::Matrix { .. } => return Some((current_base, vector, scalar)), + TypeInner::Scalar(_) | TypeInner::Vector { .. } => {} + _ => return None, + } + + let index; + (current_base, index) = match func_ctx.expressions[current_base] { + crate::Expression::Access { base, index } => (base, Index::Expression(index)), + crate::Expression::AccessIndex { base, index } => (base, Index::Static(index)), + _ => return None, + }; + + match *resolved { + TypeInner::Scalar(_) => scalar = Some(index), + TypeInner::Vector { .. } => vector = Some(index), + _ => unreachable!(), + } + } +} + /// Returns the matrix data if the access chain starting at `base`: /// - starts with an expression with resolved type of [`TypeInner::Matrix`] if `direct = true` /// - contains one or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`] @@ -4474,6 +4654,36 @@ pub(super) fn get_inner_matrix_of_struct_array_member( None } +/// Simpler version of get_inner_matrix_of_global_uniform that only looks at the +/// immediate expression, rather than traversing an access chain. +fn get_global_uniform_matrix( + module: &Module, + base: Handle, + func_ctx: &back::FunctionCtx<'_>, +) -> Option { + let base_tr = func_ctx + .resolve_type(base, &module.types) + .pointer_base_type(); + let base_ty = base_tr.as_ref().map(|tr| tr.inner_with(&module.types)); + match (&func_ctx.expressions[base], base_ty) { + ( + &crate::Expression::GlobalVariable(handle), + Some(&TypeInner::Matrix { + columns, + rows, + scalar, + }), + ) if module.global_variables[handle].space == crate::AddressSpace::Uniform => { + Some(MatrixType { + columns, + rows, + width: scalar.width, + }) + } + _ => None, + } +} + /// Returns the matrix data if the access chain starting at `base`: /// - starts with an expression with resolved type of [`TypeInner::Matrix`] /// - contains zero or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`] diff --git a/naga/src/back/mod.rs b/naga/src/back/mod.rs index d7b14475bff..0d13d63dd9b 100644 --- a/naga/src/back/mod.rs +++ b/naga/src/back/mod.rs @@ -196,6 +196,31 @@ impl FunctionCtx<'_> { } } + /// Helper method that generates a [`NameKey`](crate::proc::NameKey) for an external texture + /// function argument. + /// + /// # Panics + /// - If the function arguments are less or equal to `arg` + /// - If `self.ty` is not `FunctionType::Function`. + pub const fn external_texture_argument_key( + &self, + arg: u32, + external_texture_key: crate::proc::ExternalTextureNameKey, + ) -> crate::proc::NameKey { + match self.ty { + FunctionType::Function(handle) => { + crate::proc::NameKey::ExternalTextureFunctionArgument( + handle, + arg, + external_texture_key, + ) + } + FunctionType::EntryPoint(_) => { + panic!("External textures cannot be used as arguments to entry points") + } + } + } + /// Returns true if the given expression points to a fixed-function pipeline input. pub fn is_fixed_function_input( &self, diff --git a/naga/src/back/msl/keywords.rs b/naga/src/back/msl/keywords.rs index 512ccd91235..315af754e6c 100644 --- a/naga/src/back/msl/keywords.rs +++ b/naga/src/back/msl/keywords.rs @@ -1,7 +1,6 @@ +use crate::proc::KeywordSet; use crate::racy_lock::RacyLock; -use hashbrown::HashSet; - // MSLS - Metal Shading Language Specification: // https://developer.apple.com/metal/Metal-Shading-Language-Specification.pdf // @@ -353,19 +352,15 @@ pub const RESERVED: &[&str] = &[ super::writer::F2U32_FUNCTION, super::writer::F2I64_FUNCTION, super::writer::F2U64_FUNCTION, + super::writer::IMAGE_LOAD_EXTERNAL_FUNCTION, super::writer::IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION, + super::writer::IMAGE_SIZE_EXTERNAL_FUNCTION, super::writer::ARGUMENT_BUFFER_WRAPPER_STRUCT, + super::writer::EXTERNAL_TEXTURE_WRAPPER_STRUCT, ]; /// The above set of reserved keywords, turned into a cached HashSet. This saves /// significant time during [`Namer::reset`](crate::proc::Namer::reset). /// /// See for benchmarks. -pub static RESERVED_SET: RacyLock> = RacyLock::new(|| { - let mut set = HashSet::default(); - set.reserve(RESERVED.len()); - for &word in RESERVED { - set.insert(word); - } - set -}); +pub static RESERVED_SET: RacyLock = RacyLock::new(|| KeywordSet::from_iter(RESERVED)); diff --git a/naga/src/back/msl/mod.rs b/naga/src/back/msl/mod.rs index 7bc8289b9b8..44aedf686c4 100644 --- a/naga/src/back/msl/mod.rs +++ b/naga/src/back/msl/mod.rs @@ -43,6 +43,29 @@ additional effort and the difference is unlikely to matter.) [`BoundsCheckPolicy`]: crate::proc::BoundsCheckPolicy +## External textures + +Support for [`crate::ImageClass::External`] textures is implemented by lowering +each external texture global variable to 3 `texture2d`s, and a +constant buffer of type `NagaExternalTextureParams`. This provides up to 3 +planes of texture data (for example single planar RGBA, or separate Y, Cb, and +Cr planes), and the parameters buffer containing information describing how to +handle these correctly. The bind target to use for each of these globals is +specified via the [`BindTarget::external_texture`] field of the relevant +entries in [`EntryPointResources::resources`]. + +External textures are supported by WGSL's `textureDimensions()`, +`textureLoad()`, and `textureSampleBaseClampToEdge()` built-in functions. These +are implemented using helper functions. See the following functions for how +these are generated: + * `Writer::write_wrapped_image_query` + * `Writer::write_wrapped_image_load` + * `Writer::write_wrapped_image_sample` + +The lowered global variables for each external texture global are passed to the +entry point as separate arguments (see "Entry points" above). However, they are +then wrapped in a struct to allow them to be conveniently passed to user +defined and helper functions. See `writer::EXTERNAL_TEXTURE_WRAPPER_STRUCT`. */ use alloc::{ @@ -71,6 +94,19 @@ pub enum BindSamplerTarget { Inline(InlineSamplerIndex), } +/// Binding information for a Naga [`External`] image global variable. +/// +/// See the module documentation's section on external textures for details. +/// +/// [`External`]: crate::ir::ImageClass::External +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +pub struct BindExternalTextureTarget { + pub planes: [Slot; 3], + pub params: Slot, +} + #[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] @@ -79,12 +115,12 @@ pub struct BindTarget { pub buffer: Option, pub texture: Option, pub sampler: Option, + pub external_texture: Option, pub mutable: bool, } -#[cfg(any(feature = "serialize", feature = "deserialize"))] -#[cfg_attr(feature = "serialize", derive(serde::Serialize))] -#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +#[cfg(feature = "deserialize")] +#[derive(serde::Deserialize)] struct BindingMapSerialization { resource_binding: crate::ResourceBinding, bind_target: BindTarget, @@ -381,6 +417,17 @@ pub enum VertexFormat { Unorm8x4Bgra = 44, } +/// Defines how to advance the data in vertex buffers. +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serialize", derive(serde::Serialize))] +#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] +pub enum VertexBufferStepMode { + Constant, + #[default] + ByVertex, + ByInstance, +} + /// A mapping of vertex buffers and their attributes to shader /// locations. #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -409,9 +456,8 @@ pub struct VertexBufferMapping { pub id: u32, /// Size of the structure in bytes pub stride: u32, - /// True if the buffer is indexed by vertex, false if indexed - /// by instance. - pub indexed_by_vertex: bool, + /// Vertex buffer step mode + pub step_mode: VertexBufferStepMode, /// Vec of the attributes within the structure pub attributes: Vec, } diff --git a/naga/src/back/msl/writer.rs b/naga/src/back/msl/writer.rs index 3cb1ab2c39b..6e51f90181e 100644 --- a/naga/src/back/msl/writer.rs +++ b/naga/src/back/msl/writer.rs @@ -21,7 +21,7 @@ use crate::{ proc::{ self, index::{self, BoundsCheck}, - NameKey, TypeResolution, + ExternalTextureNameKey, NameKey, TypeResolution, }, valid, FastHashMap, FastHashSet, }; @@ -61,6 +61,8 @@ pub(crate) const F2I32_FUNCTION: &str = "naga_f2i32"; pub(crate) const F2U32_FUNCTION: &str = "naga_f2u32"; pub(crate) const F2I64_FUNCTION: &str = "naga_f2i64"; pub(crate) const F2U64_FUNCTION: &str = "naga_f2u64"; +pub(crate) const IMAGE_LOAD_EXTERNAL_FUNCTION: &str = "nagaTextureLoadExternal"; +pub(crate) const IMAGE_SIZE_EXTERNAL_FUNCTION: &str = "nagaTextureDimensionsExternal"; pub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str = "nagaTextureSampleBaseClampToEdge"; /// For some reason, Metal does not let you have `metal::texture<..>*` as a buffer argument. @@ -71,6 +73,11 @@ pub(crate) const IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION: &str = /// This allows `NagaArgumentBufferWrapper>*` to work. The astute among /// you have noticed that this should be exactly the same to the compiler, and you're correct. pub(crate) const ARGUMENT_BUFFER_WRAPPER_STRUCT: &str = "NagaArgumentBufferWrapper"; +/// Name of the struct that is declared to wrap the 3 textures and parameters +/// buffer that [`crate::ImageClass::External`] variables are lowered to, +/// allowing them to be conveniently passed to user-defined or wrapper +/// functions. The struct is declared in [`Writer::write_type_defs`]. +pub(crate) const EXTERNAL_TEXTURE_WRAPPER_STRUCT: &str = "NagaExternalTextureWrapper"; /// Write the Metal name for a Naga numeric type: scalar, vector, or matrix. /// @@ -321,6 +328,9 @@ impl Display for TypeContext<'_> { }; ("texture", "", format.into(), access) } + crate::ImageClass::External => { + return write!(out, "{EXTERNAL_TEXTURE_WRAPPER_STRUCT}"); + } }; let base_name = scalar.to_msl_name(); let array_str = if arrayed { "_array" } else { "" }; @@ -448,9 +458,16 @@ enum WrappedFunction { vector_size: Option, dst_scalar: crate::Scalar, }, + ImageLoad { + class: crate::ImageClass, + }, ImageSample { + class: crate::ImageClass, clamp_to_edge: bool, }, + ImageQuerySize { + class: crate::ImageClass, + }, } pub struct Writer { @@ -1063,6 +1080,17 @@ impl Writer { kind: crate::ScalarKind, context: &ExpressionContext, ) -> BackendResult { + if let crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } = *context.resolve_type(image) + { + write!(self.out, "{IMAGE_SIZE_EXTERNAL_FUNCTION}(")?; + self.put_expression(image, context, true)?; + write!(self.out, ")")?; + return Ok(()); + } + //Note: MSL only has separate width/height/depth queries, // so compose the result of them. let dim = match *context.resolve_type(image) { @@ -1150,7 +1178,7 @@ impl Writer { //TODO: do we support Zero on `Sampled` image classes? } _ if !has_levels => { - log::warn!("1D image can't be sampled with level {:?}", level); + log::warn!("1D image can't be sampled with level {level:?}"); } crate::SampleLevel::Exact(h) => { write!(self.out, ", {NAMESPACE}::level(")?; @@ -1320,6 +1348,19 @@ impl Writer { mut address: TexelAddress, context: &ExpressionContext, ) -> BackendResult { + if let crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } = *context.resolve_type(image) + { + write!(self.out, "{IMAGE_LOAD_EXTERNAL_FUNCTION}(")?; + self.put_expression(image, context, true)?; + write!(self.out, ", ")?; + self.put_cast_to_uint_scalar_or_vector(address.coordinate, context)?; + write!(self.out, ")")?; + return Ok(()); + } + match context.policies.image_load { proc::BoundsCheckPolicy::Restrict => { // Use the cached restricted level of detail, if any. Omit the @@ -1397,7 +1438,7 @@ impl Writer { } else { fun.to_msl() }; - write!(self.out, ".atomic_{}(", op)?; + write!(self.out, ".atomic_{op}(")?; // coordinates in IR are int, but Metal expects uint self.put_cast_to_uint_scalar_or_vector(address.coordinate, &context.expression)?; write!(self.out, ", ")?; @@ -2352,7 +2393,7 @@ impl Writer { arg1.unwrap(), 4, |writer, arg, index| { - write!(writer.out, "({}(", conversion)?; + write!(writer.out, "({conversion}(")?; writer.put_expression(arg, context, true)?; if index == 3 { write!(writer.out, ") >> 24)")?; @@ -3420,7 +3461,7 @@ impl Writer { put_numeric_type(&mut self.out, scalar, &[rows, columns])?; } TypeResolution::Value(ref other) => { - log::warn!("Type {:?} isn't a known local", other); //TEMP! + log::warn!("Type {other:?} isn't a known local"); //TEMP! return Err(Error::FeatureNotImplemented("weird local type".to_string())); } } @@ -4238,7 +4279,7 @@ impl Writer { self.namer.reset( module, &super::keywords::RESERVED_SET, - &[], + proc::CaseInsensitiveKeywordSet::empty(), &[CLAMPED_LOD_LOAD_PREFIX], &mut self.names, ); @@ -4386,15 +4427,43 @@ impl Writer { fn write_type_defs(&mut self, module: &crate::Module) -> BackendResult { let mut generated_argument_buffer_wrapper = false; + let mut generated_external_texture_wrapper = false; for (handle, ty) in module.types.iter() { - if let crate::TypeInner::BindingArray { .. } = ty.inner { - if !generated_argument_buffer_wrapper { + match ty.inner { + crate::TypeInner::BindingArray { .. } if !generated_argument_buffer_wrapper => { writeln!(self.out, "template ")?; writeln!(self.out, "struct {ARGUMENT_BUFFER_WRAPPER_STRUCT} {{")?; writeln!(self.out, "{}T {WRAPPED_ARRAY_FIELD};", back::INDENT)?; writeln!(self.out, "}};")?; generated_argument_buffer_wrapper = true; } + crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } if !generated_external_texture_wrapper => { + let params_ty_name = &self.names + [&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + writeln!(self.out, "struct {EXTERNAL_TEXTURE_WRAPPER_STRUCT} {{")?; + writeln!( + self.out, + "{}{NAMESPACE}::texture2d plane0;", + back::INDENT + )?; + writeln!( + self.out, + "{}{NAMESPACE}::texture2d plane1;", + back::INDENT + )?; + writeln!( + self.out, + "{}{NAMESPACE}::texture2d plane2;", + back::INDENT + )?; + writeln!(self.out, "{}{params_ty_name} params;", back::INDENT)?; + writeln!(self.out, "}};")?; + generated_external_texture_wrapper = true; + } + _ => {} } if !ty.needs_alias() { @@ -5075,14 +5144,14 @@ template let name = self.namer.call("unpackSnorm16x2"); writeln!( self.out, - "metal::float2 {name}(metal::ushort b0, \ - metal::ushort b1, \ - metal::ushort b2, \ - metal::ushort b3) {{" + "metal::float2 {name}(uint b0, \ + uint b1, \ + uint b2, \ + uint b3) {{" )?; writeln!( self.out, - "{}return metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2);", + "{}return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0);", back::INDENT )?; writeln!(self.out, "}}")?; @@ -5092,19 +5161,19 @@ template let name = self.namer.call("unpackSnorm16x4"); writeln!( self.out, - "metal::float4 {name}(metal::ushort b0, \ - metal::ushort b1, \ - metal::ushort b2, \ - metal::ushort b3, \ - metal::ushort b4, \ - metal::ushort b5, \ - metal::ushort b6, \ - metal::ushort b7) {{" + "metal::float4 {name}(uint b0, \ + uint b1, \ + uint b2, \ + uint b3, \ + uint b4, \ + uint b5, \ + uint b6, \ + uint b7) {{" )?; writeln!( self.out, - "{}return metal::float4(metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2), \ - metal::unpack_snorm2x16_to_float(b5 << 24 | b4 << 16 | b7 << 8 | b6));", + "{}return metal::float4(metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0), \ + metal::unpack_snorm2x16_to_float(b7 << 24 | b6 << 16 | b5 << 8 | b4));", back::INDENT )?; writeln!(self.out, "}}")?; @@ -5512,375 +5581,753 @@ template } } - pub(super) fn write_wrapped_functions( + fn write_wrapped_unary_op( &mut self, module: &crate::Module, func_ctx: &back::FunctionCtx, + op: crate::UnaryOperator, + operand: Handle, ) -> BackendResult { - for (expr_handle, expr) in func_ctx.expressions.iter() { - match *expr { - crate::Expression::Unary { op, expr: operand } => { - let operand_ty = func_ctx.resolve_type(operand, &module.types); - match op { - // Negating the TYPE_MIN of a two's complement signed integer - // type causes overflow, which is undefined behaviour in MSL. To - // avoid this we bitcast the value to unsigned and negate it, - // then bitcast back to signed. - // This adheres to the WGSL spec in that the negative of the - // type's minimum value should equal to the minimum value. - crate::UnaryOperator::Negate - if operand_ty.scalar_kind() == Some(crate::ScalarKind::Sint) => - { - let Some((vector_size, scalar)) = operand_ty.vector_size_and_scalar() - else { - continue; - }; - let wrapped = WrappedFunction::UnaryOp { - op, - ty: (vector_size, scalar), - }; - if !self.wrapped_functions.insert(wrapped) { - continue; - } - - let unsigned_scalar = crate::Scalar { - kind: crate::ScalarKind::Uint, - ..scalar - }; - let mut type_name = String::new(); - let mut unsigned_type_name = String::new(); - match vector_size { - None => { - put_numeric_type(&mut type_name, scalar, &[])?; - put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])? - } - Some(size) => { - put_numeric_type(&mut type_name, scalar, &[size])?; - put_numeric_type( - &mut unsigned_type_name, - unsigned_scalar, - &[size], - )?; - } - }; + let operand_ty = func_ctx.resolve_type(operand, &module.types); + match op { + // Negating the TYPE_MIN of a two's complement signed integer + // type causes overflow, which is undefined behaviour in MSL. To + // avoid this we bitcast the value to unsigned and negate it, + // then bitcast back to signed. + // This adheres to the WGSL spec in that the negative of the + // type's minimum value should equal to the minimum value. + crate::UnaryOperator::Negate + if operand_ty.scalar_kind() == Some(crate::ScalarKind::Sint) => + { + let Some((vector_size, scalar)) = operand_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let wrapped = WrappedFunction::UnaryOp { + op, + ty: (vector_size, scalar), + }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } - writeln!(self.out, "{type_name} {NEG_FUNCTION}({type_name} val) {{")?; - let level = back::Level(1); - writeln!(self.out, "{level}return as_type<{type_name}>(-as_type<{unsigned_type_name}>(val));")?; - writeln!(self.out, "}}")?; - writeln!(self.out)?; - } - _ => {} + let unsigned_scalar = crate::Scalar { + kind: crate::ScalarKind::Uint, + ..scalar + }; + let mut type_name = String::new(); + let mut unsigned_type_name = String::new(); + match vector_size { + None => { + put_numeric_type(&mut type_name, scalar, &[])?; + put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])? + } + Some(size) => { + put_numeric_type(&mut type_name, scalar, &[size])?; + put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[size])?; } + }; + + writeln!(self.out, "{type_name} {NEG_FUNCTION}({type_name} val) {{")?; + let level = back::Level(1); + writeln!( + self.out, + "{level}return as_type<{type_name}>(-as_type<{unsigned_type_name}>(val));" + )?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + _ => {} + } + Ok(()) + } + + fn write_wrapped_binary_op( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + expr: Handle, + op: crate::BinaryOperator, + left: Handle, + right: Handle, + ) -> BackendResult { + let expr_ty = func_ctx.resolve_type(expr, &module.types); + let left_ty = func_ctx.resolve_type(left, &module.types); + let right_ty = func_ctx.resolve_type(right, &module.types); + match (op, expr_ty.scalar_kind()) { + // Signed integer division of TYPE_MIN / -1, or signed or + // unsigned division by zero, gives an unspecified value in MSL. + // We override the divisor to 1 in these cases. + // This adheres to the WGSL spec in that: + // * TYPE_MIN / -1 == TYPE_MIN + // * x / 0 == x + ( + crate::BinaryOperator::Divide, + Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint), + ) => { + let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let wrapped = WrappedFunction::BinaryOp { + op, + left_ty: left_wrapped_ty, + right_ty: right_wrapped_ty, + }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); } - crate::Expression::Binary { op, left, right } => { - let expr_ty = func_ctx.resolve_type(expr_handle, &module.types); - let left_ty = func_ctx.resolve_type(left, &module.types); - let right_ty = func_ctx.resolve_type(right, &module.types); - match (op, expr_ty.scalar_kind()) { - // Signed integer division of TYPE_MIN / -1, or signed or - // unsigned division by zero, gives an unspecified value in MSL. - // We override the divisor to 1 in these cases. - // This adheres to the WGSL spec in that: - // * TYPE_MIN / -1 == TYPE_MIN - // * x / 0 == x - ( - crate::BinaryOperator::Divide, - Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint), - ) => { - let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else { - continue; - }; - let Some(right_wrapped_ty) = right_ty.vector_size_and_scalar() else { - continue; - }; - let wrapped = WrappedFunction::BinaryOp { - op, - left_ty: left_wrapped_ty, - right_ty: right_wrapped_ty, - }; - if !self.wrapped_functions.insert(wrapped) { - continue; - } - let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() - else { - continue; - }; - let mut type_name = String::new(); - match vector_size { - None => put_numeric_type(&mut type_name, scalar, &[])?, - Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?, - }; - writeln!( - self.out, - "{type_name} {DIV_FUNCTION}({type_name} lhs, {type_name} rhs) {{" - )?; - let level = back::Level(1); - match scalar.kind { - crate::ScalarKind::Sint => { - let min_val = match scalar.width { - 4 => crate::Literal::I32(i32::MIN), - 8 => crate::Literal::I64(i64::MIN), - _ => { - return Err(Error::GenericValidation(format!( - "Unexpected width for scalar {scalar:?}" - ))); - } - }; - write!( - self.out, - "{level}return lhs / metal::select(rhs, 1, (lhs == " - )?; - self.put_literal(min_val)?; - writeln!(self.out, " & rhs == -1) | (rhs == 0));")? - } - crate::ScalarKind::Uint => writeln!( - self.out, - "{level}return lhs / metal::select(rhs, 1u, rhs == 0u);" - )?, - _ => unreachable!(), - } - writeln!(self.out, "}}")?; - writeln!(self.out)?; - } - // Integer modulo where one or both operands are negative, or the - // divisor is zero, is undefined behaviour in MSL. To avoid this - // we use the following equation: - // - // dividend - (dividend / divisor) * divisor - // - // overriding the divisor to 1 if either it is 0, or it is -1 - // and the dividend is TYPE_MIN. - // - // This adheres to the WGSL spec in that: - // * TYPE_MIN % -1 == 0 - // * x % 0 == 0 - ( - crate::BinaryOperator::Modulo, - Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint), - ) => { - let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else { - continue; - }; - let Some((right_vector_size, right_scalar)) = - right_ty.vector_size_and_scalar() - else { - continue; - }; - let wrapped = WrappedFunction::BinaryOp { - op, - left_ty: left_wrapped_ty, - right_ty: (right_vector_size, right_scalar), - }; - if !self.wrapped_functions.insert(wrapped) { - continue; + let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let mut type_name = String::new(); + match vector_size { + None => put_numeric_type(&mut type_name, scalar, &[])?, + Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?, + }; + writeln!( + self.out, + "{type_name} {DIV_FUNCTION}({type_name} lhs, {type_name} rhs) {{" + )?; + let level = back::Level(1); + match scalar.kind { + crate::ScalarKind::Sint => { + let min_val = match scalar.width { + 4 => crate::Literal::I32(i32::MIN), + 8 => crate::Literal::I64(i64::MIN), + _ => { + return Err(Error::GenericValidation(format!( + "Unexpected width for scalar {scalar:?}" + ))); } + }; + write!( + self.out, + "{level}return lhs / metal::select(rhs, 1, (lhs == " + )?; + self.put_literal(min_val)?; + writeln!(self.out, " & rhs == -1) | (rhs == 0));")? + } + crate::ScalarKind::Uint => writeln!( + self.out, + "{level}return lhs / metal::select(rhs, 1u, rhs == 0u);" + )?, + _ => unreachable!(), + } + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + // Integer modulo where one or both operands are negative, or the + // divisor is zero, is undefined behaviour in MSL. To avoid this + // we use the following equation: + // + // dividend - (dividend / divisor) * divisor + // + // overriding the divisor to 1 if either it is 0, or it is -1 + // and the dividend is TYPE_MIN. + // + // This adheres to the WGSL spec in that: + // * TYPE_MIN % -1 == 0 + // * x % 0 == 0 + ( + crate::BinaryOperator::Modulo, + Some(crate::ScalarKind::Sint | crate::ScalarKind::Uint), + ) => { + let Some(left_wrapped_ty) = left_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let Some((right_vector_size, right_scalar)) = right_ty.vector_size_and_scalar() + else { + return Ok(()); + }; + let wrapped = WrappedFunction::BinaryOp { + op, + left_ty: left_wrapped_ty, + right_ty: (right_vector_size, right_scalar), + }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } - let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() - else { - continue; - }; - let mut type_name = String::new(); - match vector_size { - None => put_numeric_type(&mut type_name, scalar, &[])?, - Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?, - }; - let mut rhs_type_name = String::new(); - match right_vector_size { - None => put_numeric_type(&mut rhs_type_name, right_scalar, &[])?, - Some(size) => { - put_numeric_type(&mut rhs_type_name, right_scalar, &[size])? - } - }; + let Some((vector_size, scalar)) = expr_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let mut type_name = String::new(); + match vector_size { + None => put_numeric_type(&mut type_name, scalar, &[])?, + Some(size) => put_numeric_type(&mut type_name, scalar, &[size])?, + }; + let mut rhs_type_name = String::new(); + match right_vector_size { + None => put_numeric_type(&mut rhs_type_name, right_scalar, &[])?, + Some(size) => put_numeric_type(&mut rhs_type_name, right_scalar, &[size])?, + }; - writeln!( - self.out, - "{type_name} {MOD_FUNCTION}({type_name} lhs, {type_name} rhs) {{" - )?; - let level = back::Level(1); - match scalar.kind { - crate::ScalarKind::Sint => { - let min_val = match scalar.width { - 4 => crate::Literal::I32(i32::MIN), - 8 => crate::Literal::I64(i64::MIN), - _ => { - return Err(Error::GenericValidation(format!( - "Unexpected width for scalar {scalar:?}" - ))); - } - }; - write!(self.out, "{level}{rhs_type_name} divisor = metal::select(rhs, 1, (lhs == ")?; - self.put_literal(min_val)?; - writeln!(self.out, " & rhs == -1) | (rhs == 0));")?; - writeln!( - self.out, - "{level}return lhs - (lhs / divisor) * divisor;" - )? - } - crate::ScalarKind::Uint => writeln!( - self.out, - "{level}return lhs % metal::select(rhs, 1u, rhs == 0u);" - )?, - _ => unreachable!(), + writeln!( + self.out, + "{type_name} {MOD_FUNCTION}({type_name} lhs, {type_name} rhs) {{" + )?; + let level = back::Level(1); + match scalar.kind { + crate::ScalarKind::Sint => { + let min_val = match scalar.width { + 4 => crate::Literal::I32(i32::MIN), + 8 => crate::Literal::I64(i64::MIN), + _ => { + return Err(Error::GenericValidation(format!( + "Unexpected width for scalar {scalar:?}" + ))); } - writeln!(self.out, "}}")?; - writeln!(self.out)?; - } - _ => {} + }; + write!( + self.out, + "{level}{rhs_type_name} divisor = metal::select(rhs, 1, (lhs == " + )?; + self.put_literal(min_val)?; + writeln!(self.out, " & rhs == -1) | (rhs == 0));")?; + writeln!(self.out, "{level}return lhs - (lhs / divisor) * divisor;")? } + crate::ScalarKind::Uint => writeln!( + self.out, + "{level}return lhs % metal::select(rhs, 1u, rhs == 0u);" + )?, + _ => unreachable!(), } - crate::Expression::Math { - fun, - arg, - arg1: _, - arg2: _, - arg3: _, - } => { - let arg_ty = func_ctx.resolve_type(arg, &module.types); - match fun { - // Taking the absolute value of the TYPE_MIN of a two's - // complement signed integer type causes overflow, which is - // undefined behaviour in MSL. To avoid this, when the value is - // negative we bitcast the value to unsigned and negate it, then - // bitcast back to signed. - // This adheres to the WGSL spec in that the absolute of the - // type's minimum value should equal to the minimum value. - crate::MathFunction::Abs - if arg_ty.scalar_kind() == Some(crate::ScalarKind::Sint) => - { - let Some((vector_size, scalar)) = arg_ty.vector_size_and_scalar() - else { - continue; - }; - let wrapped = WrappedFunction::Math { - fun, - arg_ty: (vector_size, scalar), - }; - if !self.wrapped_functions.insert(wrapped) { - continue; - } + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + _ => {} + } + Ok(()) + } - let unsigned_scalar = crate::Scalar { - kind: crate::ScalarKind::Uint, - ..scalar - }; - let mut type_name = String::new(); - let mut unsigned_type_name = String::new(); - match vector_size { - None => { - put_numeric_type(&mut type_name, scalar, &[])?; - put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])? - } - Some(size) => { - put_numeric_type(&mut type_name, scalar, &[size])?; - put_numeric_type( - &mut unsigned_type_name, - unsigned_scalar, - &[size], - )?; - } - }; + #[allow(clippy::too_many_arguments)] + fn write_wrapped_math_function( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + fun: crate::MathFunction, + arg: Handle, + _arg1: Option>, + _arg2: Option>, + _arg3: Option>, + ) -> BackendResult { + let arg_ty = func_ctx.resolve_type(arg, &module.types); + match fun { + // Taking the absolute value of the TYPE_MIN of a two's + // complement signed integer type causes overflow, which is + // undefined behaviour in MSL. To avoid this, when the value is + // negative we bitcast the value to unsigned and negate it, then + // bitcast back to signed. + // This adheres to the WGSL spec in that the absolute of the + // type's minimum value should equal to the minimum value. + crate::MathFunction::Abs if arg_ty.scalar_kind() == Some(crate::ScalarKind::Sint) => { + let Some((vector_size, scalar)) = arg_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let wrapped = WrappedFunction::Math { + fun, + arg_ty: (vector_size, scalar), + }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } - writeln!(self.out, "{type_name} {ABS_FUNCTION}({type_name} val) {{")?; - let level = back::Level(1); - writeln!(self.out, "{level}return metal::select(as_type<{type_name}>(-as_type<{unsigned_type_name}>(val)), val, val >= 0);")?; - writeln!(self.out, "}}")?; - writeln!(self.out)?; - } - _ => {} + let unsigned_scalar = crate::Scalar { + kind: crate::ScalarKind::Uint, + ..scalar + }; + let mut type_name = String::new(); + let mut unsigned_type_name = String::new(); + match vector_size { + None => { + put_numeric_type(&mut type_name, scalar, &[])?; + put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[])? } - } - crate::Expression::As { - expr, - kind, - convert: Some(width), - } => { - // Avoid undefined behaviour when casting from a float to integer - // when the value is out of range for the target type. Additionally - // ensure we clamp to the correct value as per the WGSL spec. - // - // https://www.w3.org/TR/WGSL/#floating-point-conversion: - // * If X is exactly representable in the target type T, then the - // result is that value. - // * Otherwise, the result is the value in T closest to - // truncate(X) and also exactly representable in the original - // floating point type. - let src_ty = func_ctx.resolve_type(expr, &module.types); - let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else { - continue; - }; - let dst_scalar = crate::Scalar { kind, width }; - if src_scalar.kind != crate::ScalarKind::Float - || (dst_scalar.kind != crate::ScalarKind::Sint - && dst_scalar.kind != crate::ScalarKind::Uint) - { - continue; + Some(size) => { + put_numeric_type(&mut type_name, scalar, &[size])?; + put_numeric_type(&mut unsigned_type_name, unsigned_scalar, &[size])?; } - let wrapped = WrappedFunction::Cast { - src_scalar, - vector_size, - dst_scalar, - }; - if !self.wrapped_functions.insert(wrapped) { - continue; - } - let (min, max) = proc::min_max_float_representable_by(src_scalar, dst_scalar); + }; - let mut src_type_name = String::new(); - match vector_size { - None => put_numeric_type(&mut src_type_name, src_scalar, &[])?, - Some(size) => put_numeric_type(&mut src_type_name, src_scalar, &[size])?, - }; - let mut dst_type_name = String::new(); - match vector_size { - None => put_numeric_type(&mut dst_type_name, dst_scalar, &[])?, - Some(size) => put_numeric_type(&mut dst_type_name, dst_scalar, &[size])?, - }; - let fun_name = match dst_scalar { - crate::Scalar::I32 => F2I32_FUNCTION, - crate::Scalar::U32 => F2U32_FUNCTION, - crate::Scalar::I64 => F2I64_FUNCTION, - crate::Scalar::U64 => F2U64_FUNCTION, - _ => unreachable!(), - }; + writeln!(self.out, "{type_name} {ABS_FUNCTION}({type_name} val) {{")?; + let level = back::Level(1); + writeln!(self.out, "{level}return metal::select(as_type<{type_name}>(-as_type<{unsigned_type_name}>(val)), val, val >= 0);")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + _ => {} + } + Ok(()) + } - writeln!( - self.out, - "{dst_type_name} {fun_name}({src_type_name} value) {{" - )?; - let level = back::Level(1); - write!( - self.out, - "{level}return static_cast<{dst_type_name}>({NAMESPACE}::clamp(value, " + fn write_wrapped_cast( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + expr: Handle, + kind: crate::ScalarKind, + convert: Option, + ) -> BackendResult { + // Avoid undefined behaviour when casting from a float to integer + // when the value is out of range for the target type. Additionally + // ensure we clamp to the correct value as per the WGSL spec. + // + // https://www.w3.org/TR/WGSL/#floating-point-conversion: + // * If X is exactly representable in the target type T, then the + // result is that value. + // * Otherwise, the result is the value in T closest to + // truncate(X) and also exactly representable in the original + // floating point type. + let src_ty = func_ctx.resolve_type(expr, &module.types); + let Some(width) = convert else { + return Ok(()); + }; + let Some((vector_size, src_scalar)) = src_ty.vector_size_and_scalar() else { + return Ok(()); + }; + let dst_scalar = crate::Scalar { kind, width }; + if src_scalar.kind != crate::ScalarKind::Float + || (dst_scalar.kind != crate::ScalarKind::Sint + && dst_scalar.kind != crate::ScalarKind::Uint) + { + return Ok(()); + } + let wrapped = WrappedFunction::Cast { + src_scalar, + vector_size, + dst_scalar, + }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } + let (min, max) = proc::min_max_float_representable_by(src_scalar, dst_scalar); + + let mut src_type_name = String::new(); + match vector_size { + None => put_numeric_type(&mut src_type_name, src_scalar, &[])?, + Some(size) => put_numeric_type(&mut src_type_name, src_scalar, &[size])?, + }; + let mut dst_type_name = String::new(); + match vector_size { + None => put_numeric_type(&mut dst_type_name, dst_scalar, &[])?, + Some(size) => put_numeric_type(&mut dst_type_name, dst_scalar, &[size])?, + }; + let fun_name = match dst_scalar { + crate::Scalar::I32 => F2I32_FUNCTION, + crate::Scalar::U32 => F2U32_FUNCTION, + crate::Scalar::I64 => F2I64_FUNCTION, + crate::Scalar::U64 => F2U64_FUNCTION, + _ => unreachable!(), + }; + + writeln!( + self.out, + "{dst_type_name} {fun_name}({src_type_name} value) {{" + )?; + let level = back::Level(1); + write!( + self.out, + "{level}return static_cast<{dst_type_name}>({NAMESPACE}::clamp(value, " + )?; + self.put_literal(min)?; + write!(self.out, ", ")?; + self.put_literal(max)?; + writeln!(self.out, "));")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + Ok(()) + } + + /// Helper function used by [`Self::write_wrapped_image_load`] and + /// [`Self::write_wrapped_image_sample`] to write the shared YUV to RGB + /// conversion code for external textures. Expects the preceding code to + /// declare the Y component as a `float` variable of name `y`, the UV + /// components as a `float2` variable of name `uv`, and the external + /// texture params as a variable of name `params`. The emitted code will + /// return the result. + fn write_convert_yuv_to_rgb_and_return( + &mut self, + level: back::Level, + y: &str, + uv: &str, + params: &str, + ) -> BackendResult { + let l1 = level; + let l2 = l1.next(); + + // Convert from YUV to non-linear RGB in the source color space. + writeln!( + self.out, + "{l1}float3 srcGammaRgb = ({params}.yuv_conversion_matrix * float4({y}, {uv}, 1.0)).rgb;" + )?; + + // Apply the inverse of the source transfer function to convert to + // linear RGB in the source color space. + writeln!(self.out, "{l1}float3 srcLinearRgb = {NAMESPACE}::select(")?; + writeln!(self.out, "{l2}{NAMESPACE}::pow((srcGammaRgb + {params}.src_tf.a - 1.0) / {params}.src_tf.a, {params}.src_tf.g),")?; + writeln!(self.out, "{l2}srcGammaRgb / {params}.src_tf.k,")?; + writeln!( + self.out, + "{l2}srcGammaRgb < {params}.src_tf.k * {params}.src_tf.b);" + )?; + + // Multiply by the gamut conversion matrix to convert to linear RGB in + // the destination color space. + writeln!( + self.out, + "{l1}float3 dstLinearRgb = {params}.gamut_conversion_matrix * srcLinearRgb;" + )?; + + // Finally, apply the dest transfer function to convert to non-linear + // RGB in the destination color space, and return the result. + writeln!(self.out, "{l1}float3 dstGammaRgb = {NAMESPACE}::select(")?; + writeln!(self.out, "{l2}{params}.dst_tf.a * {NAMESPACE}::pow(dstLinearRgb, 1.0 / {params}.dst_tf.g) - ({params}.dst_tf.a - 1),")?; + writeln!(self.out, "{l2}{params}.dst_tf.k * dstLinearRgb,")?; + writeln!(self.out, "{l2}dstLinearRgb < {params}.dst_tf.b);")?; + + writeln!(self.out, "{l1}return float4(dstGammaRgb, 1.0);")?; + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn write_wrapped_image_load( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + image: Handle, + _coordinate: Handle, + _array_index: Option>, + _sample: Option>, + _level: Option>, + ) -> BackendResult { + // We currently only need to wrap image loads for external textures + let class = match *func_ctx.resolve_type(image, &module.types) { + crate::TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; + if class != crate::ImageClass::External { + return Ok(()); + } + let wrapped = WrappedFunction::ImageLoad { class }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } + + writeln!(self.out, "float4 {IMAGE_LOAD_EXTERNAL_FUNCTION}({EXTERNAL_TEXTURE_WRAPPER_STRUCT} tex, uint2 coords) {{")?; + let l1 = back::Level(1); + let l2 = l1.next(); + let l3 = l2.next(); + writeln!( + self.out, + "{l1}uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height());" + )?; + // Clamp coords to provided size of external texture to prevent OOB + // read. If params.size is zero then clamp to the actual size of the + // texture. + writeln!( + self.out, + "{l1}uint2 cropped_size = {NAMESPACE}::any(tex.params.size != 0) ? tex.params.size : plane0_size;" + )?; + writeln!( + self.out, + "{l1}coords = {NAMESPACE}::min(coords, cropped_size - 1);" + )?; + + // Apply load transformation + writeln!(self.out, "{l1}uint2 plane0_coords = uint2({NAMESPACE}::round(tex.params.load_transform * float3(float2(coords), 1.0)));")?; + writeln!(self.out, "{l1}if (tex.params.num_planes == 1u) {{")?; + // For single plane, simply read from plane0 + writeln!(self.out, "{l2}return tex.plane0.read(plane0_coords);")?; + writeln!(self.out, "{l1}}} else {{")?; + + // Chroma planes may be subsampled so we must scale the coords accordingly. + writeln!( + self.out, + "{l2}uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height());" + )?; + writeln!(self.out, "{l2}uint2 plane1_coords = uint2({NAMESPACE}::floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size)));")?; + + // For multi-plane, read the Y value from plane 0 + writeln!(self.out, "{l2}float y = tex.plane0.read(plane0_coords).x;")?; + + writeln!(self.out, "{l2}float2 uv;")?; + writeln!(self.out, "{l2}if (tex.params.num_planes == 2u) {{")?; + // For 2 planes, read UV from interleaved plane 1 + writeln!(self.out, "{l3}uv = tex.plane1.read(plane1_coords).xy;")?; + writeln!(self.out, "{l2}}} else {{")?; + // For 3 planes, read U and V from planes 1 and 2 respectively + writeln!( + self.out, + "{l2}uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height());" + )?; + writeln!(self.out, "{l2}uint2 plane2_coords = uint2({NAMESPACE}::floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size)));")?; + writeln!( + self.out, + "{l3}uv = float2(tex.plane1.read(plane1_coords).x, tex.plane2.read(plane2_coords).x);" + )?; + writeln!(self.out, "{l2}}}")?; + + self.write_convert_yuv_to_rgb_and_return(l2, "y", "uv", "tex.params")?; + + writeln!(self.out, "{l1}}}")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + Ok(()) + } + + #[allow(clippy::too_many_arguments)] + fn write_wrapped_image_sample( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + image: Handle, + _sampler: Handle, + _gather: Option, + _coordinate: Handle, + _array_index: Option>, + _offset: Option>, + _level: crate::SampleLevel, + _depth_ref: Option>, + clamp_to_edge: bool, + ) -> BackendResult { + // We currently only need to wrap textureSampleBaseClampToEdge, for + // both sampled and external textures. + if !clamp_to_edge { + return Ok(()); + } + let class = match *func_ctx.resolve_type(image, &module.types) { + crate::TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; + let wrapped = WrappedFunction::ImageSample { + class, + clamp_to_edge: true, + }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } + match class { + crate::ImageClass::External => { + writeln!(self.out, "float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}({EXTERNAL_TEXTURE_WRAPPER_STRUCT} tex, {NAMESPACE}::sampler samp, float2 coords) {{")?; + let l1 = back::Level(1); + let l2 = l1.next(); + let l3 = l2.next(); + writeln!(self.out, "{l1}uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height());")?; + writeln!( + self.out, + "{l1}coords = tex.params.sample_transform * float3(coords, 1.0);" + )?; + + // Calculate the sample bounds. The purported size of the texture + // (params.size) is irrelevant here as we are dealing with normalized + // coordinates. Usually we would clamp to (0,0)..(1,1). However, we must + // apply the sample transformation to that, also bearing in mind that it + // may contain a flip on either axis. We calculate and adjust for the + // half-texel separately for each plane as it depends on the actual + // texture size which may vary between planes. + writeln!( + self.out, + "{l1}float2 bounds_min = tex.params.sample_transform * float3(0.0, 0.0, 1.0);" + )?; + writeln!( + self.out, + "{l1}float2 bounds_max = tex.params.sample_transform * float3(1.0, 1.0, 1.0);" + )?; + writeln!(self.out, "{l1}float4 bounds = float4({NAMESPACE}::min(bounds_min, bounds_max), {NAMESPACE}::max(bounds_min, bounds_max));")?; + writeln!( + self.out, + "{l1}float2 plane0_half_texel = float2(0.5, 0.5) / float2(plane0_size);" + )?; + writeln!( + self.out, + "{l1}float2 plane0_coords = {NAMESPACE}::clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel);" + )?; + writeln!(self.out, "{l1}if (tex.params.num_planes == 1u) {{")?; + // For single plane, simply sample from plane0 + writeln!( + self.out, + "{l2}return tex.plane0.sample(samp, plane0_coords, {NAMESPACE}::level(0.0f));" + )?; + writeln!(self.out, "{l1}}} else {{")?; + writeln!(self.out, "{l2}uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height());")?; + writeln!( + self.out, + "{l2}float2 plane1_half_texel = float2(0.5, 0.5) / float2(plane1_size);" + )?; + writeln!( + self.out, + "{l2}float2 plane1_coords = {NAMESPACE}::clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel);" + )?; + + // For multi-plane, sample the Y value from plane 0 + writeln!( + self.out, + "{l2}float y = tex.plane0.sample(samp, plane0_coords, {NAMESPACE}::level(0.0f)).r;" + )?; + writeln!(self.out, "{l2}float2 uv = float2(0.0, 0.0);")?; + writeln!(self.out, "{l2}if (tex.params.num_planes == 2u) {{")?; + // For 2 planes, sample UV from interleaved plane 1 + writeln!( + self.out, + "{l3}uv = tex.plane1.sample(samp, plane1_coords, {NAMESPACE}::level(0.0f)).xy;" + )?; + writeln!(self.out, "{l2}}} else {{")?; + // For 3 planes, sample U and V from planes 1 and 2 respectively + writeln!(self.out, "{l3}uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height());")?; + writeln!( + self.out, + "{l3}float2 plane2_half_texel = float2(0.5, 0.5) / float2(plane2_size);" + )?; + writeln!( + self.out, + "{l3}float2 plane2_coords = {NAMESPACE}::clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane1_half_texel);" + )?; + writeln!(self.out, "{l3}uv.x = tex.plane1.sample(samp, plane1_coords, {NAMESPACE}::level(0.0f)).x;")?; + writeln!(self.out, "{l3}uv.y = tex.plane2.sample(samp, plane2_coords, {NAMESPACE}::level(0.0f)).x;")?; + writeln!(self.out, "{l2}}}")?; + + self.write_convert_yuv_to_rgb_and_return(l2, "y", "uv", "tex.params")?; + + writeln!(self.out, "{l1}}}")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + _ => { + writeln!(self.out, "{NAMESPACE}::float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}({NAMESPACE}::texture2d tex, {NAMESPACE}::sampler samp, {NAMESPACE}::float2 coords) {{")?; + let l1 = back::Level(1); + writeln!(self.out, "{l1}{NAMESPACE}::float2 half_texel = 0.5 / {NAMESPACE}::float2(tex.get_width(0u), tex.get_height(0u));")?; + writeln!( + self.out, + "{l1}return tex.sample(samp, {NAMESPACE}::clamp(coords, half_texel, 1.0 - half_texel), {NAMESPACE}::level(0.0));" + )?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + } + } + Ok(()) + } + + fn write_wrapped_image_query( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + image: Handle, + query: crate::ImageQuery, + ) -> BackendResult { + // We currently only need to wrap size image queries for external textures + if !matches!(query, crate::ImageQuery::Size { .. }) { + return Ok(()); + } + let class = match *func_ctx.resolve_type(image, &module.types) { + crate::TypeInner::Image { class, .. } => class, + _ => unreachable!(), + }; + if class != crate::ImageClass::External { + return Ok(()); + } + let wrapped = WrappedFunction::ImageQuerySize { class }; + if !self.wrapped_functions.insert(wrapped) { + return Ok(()); + } + writeln!( + self.out, + "uint2 {IMAGE_SIZE_EXTERNAL_FUNCTION}({EXTERNAL_TEXTURE_WRAPPER_STRUCT} tex) {{" + )?; + let l1 = back::Level(1); + let l2 = l1.next(); + writeln!( + self.out, + "{l1}if ({NAMESPACE}::any(tex.params.size != uint2(0u))) {{" + )?; + writeln!(self.out, "{l2}return tex.params.size;")?; + writeln!(self.out, "{l1}}} else {{")?; + // params.size == (0, 0) indicates to query and return plane 0's actual size + writeln!( + self.out, + "{l2}return uint2(tex.plane0.get_width(), tex.plane0.get_height());" + )?; + writeln!(self.out, "{l1}}}")?; + writeln!(self.out, "}}")?; + writeln!(self.out)?; + Ok(()) + } + + pub(super) fn write_wrapped_functions( + &mut self, + module: &crate::Module, + func_ctx: &back::FunctionCtx, + ) -> BackendResult { + for (expr_handle, expr) in func_ctx.expressions.iter() { + match *expr { + crate::Expression::Unary { op, expr: operand } => { + self.write_wrapped_unary_op(module, func_ctx, op, operand)?; + } + crate::Expression::Binary { op, left, right } => { + self.write_wrapped_binary_op(module, func_ctx, expr_handle, op, left, right)?; + } + crate::Expression::Math { + fun, + arg, + arg1, + arg2, + arg3, + } => { + self.write_wrapped_math_function(module, func_ctx, fun, arg, arg1, arg2, arg3)?; + } + crate::Expression::As { + expr, + kind, + convert, + } => { + self.write_wrapped_cast(module, func_ctx, expr, kind, convert)?; + } + crate::Expression::ImageLoad { + image, + coordinate, + array_index, + sample, + level, + } => { + self.write_wrapped_image_load( + module, + func_ctx, + image, + coordinate, + array_index, + sample, + level, )?; - self.put_literal(min)?; - write!(self.out, ", ")?; - self.put_literal(max)?; - writeln!(self.out, "));")?; - writeln!(self.out, "}}")?; - writeln!(self.out)?; } crate::Expression::ImageSample { - clamp_to_edge: true, - .. + image, + sampler, + gather, + coordinate, + array_index, + offset, + level, + depth_ref, + clamp_to_edge, } => { - let wrapped = WrappedFunction::ImageSample { - clamp_to_edge: true, - }; - if !self.wrapped_functions.insert(wrapped) { - continue; - } - - writeln!(self.out, "{NAMESPACE}::float4 {IMAGE_SAMPLE_BASE_CLAMP_TO_EDGE_FUNCTION}({NAMESPACE}::texture2d tex, {NAMESPACE}::sampler samp, {NAMESPACE}::float2 coords) {{")?; - let l1 = back::Level(1); - writeln!(self.out, "{l1}{NAMESPACE}::float2 half_texel = 0.5 / {NAMESPACE}::float2(tex.get_width(0u), tex.get_height(0u));")?; - writeln!( - self.out, - "{l1}return tex.sample(samp, {NAMESPACE}::clamp(coords, half_texel, 1.0 - half_texel), {NAMESPACE}::level(0.0));" + self.write_wrapped_image_sample( + module, + func_ctx, + image, + sampler, + gather, + coordinate, + array_index, + offset, + level, + depth_ref, + clamp_to_edge, )?; - writeln!(self.out, "}}")?; - writeln!(self.out)?; + } + crate::Expression::ImageQuery { image, query } => { + self.write_wrapped_image_query(module, func_ctx, image, query)?; } _ => {} } @@ -5912,7 +6359,7 @@ template struct VertexBufferMappingResolved<'a> { id: u32, stride: u32, - indexed_by_vertex: bool, + step_mode: back::msl::VertexBufferStepMode, ty_name: String, param_name: String, elem_name: String, @@ -5948,10 +6395,14 @@ template "Vertex pulling requires a non-zero buffer stride." ); - if vbm.indexed_by_vertex { - needs_vertex_id = true; - } else { - needs_instance_id = true; + match vbm.step_mode { + back::msl::VertexBufferStepMode::Constant => {} + back::msl::VertexBufferStepMode::ByVertex => { + needs_vertex_id = true; + } + back::msl::VertexBufferStepMode::ByInstance => { + needs_instance_id = true; + } } let buffer_ty = self.namer.call(format!("vb_{buffer_id}_type").as_str()); @@ -5961,7 +6412,7 @@ template vbm_resolved.push(VertexBufferMappingResolved { id: buffer_id, stride: buffer_stride, - indexed_by_vertex: vbm.indexed_by_vertex, + step_mode: vbm.step_mode, ty_name: buffer_ty, param_name: buffer_param, elem_name: buffer_elem, @@ -6211,6 +6662,10 @@ template // so that binding arrays fall to the buffer location. match module.types[var.ty].inner { + crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } => target.external_texture.is_some(), crate::TypeInner::Image { .. } => target.texture.is_some(), crate::TypeInner::Sampler { .. } => { target.sampler.is_some() @@ -6449,16 +6904,25 @@ template // Write the entry point function's name, and begin its argument list. writeln!(self.out, "{em_str} {result_type_name} {fun_name}(")?; + let mut is_first_argument = true; + let mut separator = || { + if is_first_argument { + is_first_argument = false; + ' ' + } else { + ',' + } + }; // If we have produced a struct holding the `EntryPoint`'s // `Function`'s arguments' varyings, pass that struct first. if has_varyings { writeln!( self.out, - " {stage_in_name} {varyings_member_name} [[stage_in]]" + "{} {stage_in_name} {varyings_member_name} [[stage_in]]", + separator() )?; - is_first_argument = false; } let mut local_invocation_id = None; @@ -6498,13 +6962,7 @@ template }; let resolved = options.resolve_local_binding(binding, in_mode)?; - let separator = if is_first_argument { - is_first_argument = false; - ' ' - } else { - ',' - }; - write!(self.out, "{separator} {ty_name} {name}")?; + write!(self.out, "{} {ty_name} {name}", separator())?; resolved.try_fmt(&mut self.out)?; writeln!(self.out)?; } @@ -6513,15 +6971,9 @@ template self.need_workgroup_variables_initialization(options, ep, module, fun_info); if need_workgroup_variables_initialization && local_invocation_id.is_none() { - let separator = if is_first_argument { - is_first_argument = false; - ' ' - } else { - ',' - }; writeln!( self.out, - "{separator} {NAMESPACE}::uint3 __local_invocation_id [[thread_position_in_threadgroup]]" + "{} {NAMESPACE}::uint3 __local_invocation_id [[thread_position_in_threadgroup]]", separator() )?; } @@ -6637,6 +7089,11 @@ template "read-write textures".to_string(), )); } + crate::ImageClass::External => { + return Err(Error::UnsupportedArrayOf( + "external textures".to_string(), + )); + } }, _ => { return Err(Error::UnsupportedArrayOfType(base)); @@ -6663,49 +7120,84 @@ template } } - let tyvar = TypedGlobalVariable { - module, - names: &self.names, - handle, - usage, - reference: true, - }; - let separator = if is_first_argument { - is_first_argument = false; - ' ' - } else { - ',' - }; - write!(self.out, "{separator} ")?; - tyvar.try_fmt(&mut self.out)?; - if let Some(resolved) = resolved { - resolved.try_fmt(&mut self.out)?; - } - if let Some(value) = var.init { - write!(self.out, " = ")?; - self.put_const_expression(value, module, mod_info, &module.global_expressions)?; + match module.types[var.ty].inner { + crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } => { + // External texture global variables get lowered to 3 textures + // and a constant buffer. We must emit a separate argument for + // each of these. + let target = match resolved { + Some(back::msl::ResolvedBinding::Resource(target)) => { + target.external_texture + } + _ => None, + }; + + for i in 0..3 { + write!(self.out, "{} ", separator())?; + + let plane_name = &self.names[&NameKey::ExternalTextureGlobalVariable( + handle, + ExternalTextureNameKey::Plane(i), + )]; + write!( + self.out, + "{NAMESPACE}::texture2d {plane_name}" + )?; + if let Some(ref target) = target { + write!(self.out, " [[texture({})]]", target.planes[i])?; + } + writeln!(self.out)?; + } + let params_ty_name = &self.names + [&NameKey::Type(module.special_types.external_texture_params.unwrap())]; + let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable( + handle, + ExternalTextureNameKey::Params, + )]; + write!(self.out, "{} ", separator())?; + write!(self.out, "constant {params_ty_name}& {params_name}")?; + if let Some(ref target) = target { + write!(self.out, " [[buffer({})]]", target.params)?; + } + } + _ => { + let tyvar = TypedGlobalVariable { + module, + names: &self.names, + handle, + usage, + reference: true, + }; + write!(self.out, "{} ", separator())?; + tyvar.try_fmt(&mut self.out)?; + if let Some(resolved) = resolved { + resolved.try_fmt(&mut self.out)?; + } + if let Some(value) = var.init { + write!(self.out, " = ")?; + self.put_const_expression( + value, + module, + mod_info, + &module.global_expressions, + )?; + } + } } writeln!(self.out)?; } if do_vertex_pulling { - assert!(needs_vertex_id || needs_instance_id); - - let mut separator = if is_first_argument { - is_first_argument = false; - ' ' - } else { - ',' - }; - if needs_vertex_id && v_existing_id.is_none() { // Write the [[vertex_id]] argument. - writeln!(self.out, "{separator} uint {v_id} [[vertex_id]]")?; - separator = ','; + writeln!(self.out, "{} uint {v_id} [[vertex_id]]", separator())?; } if needs_instance_id && i_existing_id.is_none() { - writeln!(self.out, "{separator} uint {i_id} [[instance_id]]")?; + writeln!(self.out, "{} uint {i_id} [[instance_id]]", separator())?; } // Iterate vbm_resolved, output one argument for every vertex buffer, @@ -6716,7 +7208,8 @@ template let param_name = &vbm.param_name; writeln!( self.out, - ", const device {ty_name}* {param_name} [[buffer({id})]]" + "{} const device {ty_name}* {param_name} [[buffer({id})]]", + separator() )?; } } @@ -6726,10 +7219,10 @@ template if needs_buffer_sizes { // this is checked earlier let resolved = options.resolve_sizes_buffer(ep).unwrap(); - let separator = if is_first_argument { ' ' } else { ',' }; write!( self.out, - "{separator} constant _mslBufferSizes& _buffer_sizes", + "{} constant _mslBufferSizes& _buffer_sizes", + separator() )?; resolved.try_fmt(&mut self.out)?; writeln!(self.out)?; @@ -6768,16 +7261,22 @@ template let idx = &vbm.id; let stride = &vbm.stride; - let index_name = if vbm.indexed_by_vertex { - if let Some(ref name) = v_existing_id { - name - } else { - &v_id + let index_name = match vbm.step_mode { + back::msl::VertexBufferStepMode::Constant => "0", + back::msl::VertexBufferStepMode::ByVertex => { + if let Some(ref name) = v_existing_id { + name + } else { + &v_id + } + } + back::msl::VertexBufferStepMode::ByInstance => { + if let Some(ref name) = i_existing_id { + name + } else { + &i_id + } } - } else if let Some(ref name) = i_existing_id { - name - } else { - &i_id }; write!( self.out, @@ -6925,9 +7424,9 @@ template } }; } else if let Some(ref binding) = var.binding { - // write an inline sampler let resolved = options.resolve_resource_binding(ep, binding).unwrap(); if let Some(sampler) = resolved.as_inline_sampler(options) { + // write an inline sampler let name = &self.names[&NameKey::GlobalVariable(handle)]; writeln!( self.out, @@ -6938,6 +7437,33 @@ template )?; self.put_inline_sampler_properties(back::Level(2), sampler)?; writeln!(self.out, "{});", back::INDENT)?; + } else if let crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } = module.types[var.ty].inner + { + // Wrap the individual arguments for each external texture global + // in a struct which can be easily passed around. + let wrapper_name = &self.names[&NameKey::GlobalVariable(handle)]; + let l1 = back::Level(1); + let l2 = l1.next(); + writeln!( + self.out, + "{l1}const {EXTERNAL_TEXTURE_WRAPPER_STRUCT} {wrapper_name} {{" + )?; + for i in 0..3 { + let plane_name = &self.names[&NameKey::ExternalTextureGlobalVariable( + handle, + ExternalTextureNameKey::Plane(i), + )]; + writeln!(self.out, "{l2}.plane{i} = {plane_name},")?; + } + let params_name = &self.names[&NameKey::ExternalTextureGlobalVariable( + handle, + ExternalTextureNameKey::Params, + )]; + writeln!(self.out, "{l2}.params = {params_name},")?; + writeln!(self.out, "{l1}}};")?; } } } diff --git a/naga/src/back/spv/block.rs b/naga/src/back/spv/block.rs index 7ec659e1d90..7758d86c414 100644 --- a/naga/src/back/spv/block.rs +++ b/naga/src/back/spv/block.rs @@ -237,7 +237,7 @@ impl Writer { } }; - body.push(Instruction::store(res_member.id, member_value_id, None)); + self.store_io_with_f16_polyfill(body, res_member.id, member_value_id); match res_member.built_in { Some(crate::BuiltIn::Position { .. }) @@ -675,7 +675,7 @@ impl BlockContext<'_> { load_id } ref other => { - log::error!("Unable to access index of {:?}", other); + log::error!("Unable to access index of {other:?}"); return Err(Error::FeatureNotImplemented("access index for type")); } } @@ -1893,7 +1893,7 @@ impl BlockContext<'_> { crate::TypeInner::Scalar(scalar) => (scalar, None), crate::TypeInner::Vector { scalar, size } => (scalar, Some(size)), ref other => { - log::error!("As source {:?}", other); + log::error!("As source {other:?}"); return Err(Error::Validation("Unexpected Expression::As source")); } }; @@ -2979,62 +2979,69 @@ impl BlockContext<'_> { ref accept, ref reject, } => { - let condition_id = self.cached[condition]; + // In spirv 1.6, in a conditional branch the two block ids + // of the branches can't have the same label. If `accept` + // and `reject` are both empty (e.g. in `if (condition) {}`) + // merge id will be both labels. Because both branches are + // empty, we can skip the if statement. + if !(accept.is_empty() && reject.is_empty()) { + let condition_id = self.cached[condition]; + + let merge_id = self.gen_id(); + block.body.push(Instruction::selection_merge( + merge_id, + spirv::SelectionControl::NONE, + )); - let merge_id = self.gen_id(); - block.body.push(Instruction::selection_merge( - merge_id, - spirv::SelectionControl::NONE, - )); + let accept_id = if accept.is_empty() { + None + } else { + Some(self.gen_id()) + }; + let reject_id = if reject.is_empty() { + None + } else { + Some(self.gen_id()) + }; - let accept_id = if accept.is_empty() { - None - } else { - Some(self.gen_id()) - }; - let reject_id = if reject.is_empty() { - None - } else { - Some(self.gen_id()) - }; + self.function.consume( + block, + Instruction::branch_conditional( + condition_id, + accept_id.unwrap_or(merge_id), + reject_id.unwrap_or(merge_id), + ), + ); - self.function.consume( - block, - Instruction::branch_conditional( - condition_id, - accept_id.unwrap_or(merge_id), - reject_id.unwrap_or(merge_id), - ), - ); + if let Some(block_id) = accept_id { + // We can ignore the `BlockExitDisposition` returned here because, + // even if `merge_id` is not actually reachable, it is always + // referred to by the `OpSelectionMerge` instruction we emitted + // earlier. + let _ = self.write_block( + block_id, + accept, + BlockExit::Branch { target: merge_id }, + loop_context, + debug_info, + )?; + } + if let Some(block_id) = reject_id { + // We can ignore the `BlockExitDisposition` returned here because, + // even if `merge_id` is not actually reachable, it is always + // referred to by the `OpSelectionMerge` instruction we emitted + // earlier. + let _ = self.write_block( + block_id, + reject, + BlockExit::Branch { target: merge_id }, + loop_context, + debug_info, + )?; + } - if let Some(block_id) = accept_id { - // We can ignore the `BlockExitDisposition` returned here because, - // even if `merge_id` is not actually reachable, it is always - // referred to by the `OpSelectionMerge` instruction we emitted - // earlier. - let _ = self.write_block( - block_id, - accept, - BlockExit::Branch { target: merge_id }, - loop_context, - debug_info, - )?; - } - if let Some(block_id) = reject_id { - // We can ignore the `BlockExitDisposition` returned here because, - // even if `merge_id` is not actually reachable, it is always - // referred to by the `OpSelectionMerge` instruction we emitted - // earlier. - let _ = self.write_block( - block_id, - reject, - BlockExit::Branch { target: merge_id }, - loop_context, - debug_info, - )?; + block = Block::new(merge_id); } - - block = Block::new(merge_id); } Statement::Switch { selector, diff --git a/naga/src/back/spv/f16_polyfill.rs b/naga/src/back/spv/f16_polyfill.rs new file mode 100644 index 00000000000..824490265af --- /dev/null +++ b/naga/src/back/spv/f16_polyfill.rs @@ -0,0 +1,104 @@ +/*! +This module provides functionality for polyfilling `f16` input/output variables +when the `StorageInputOutput16` capability is not available or disabled. + +It works by: + +1. Declaring `f16` I/O variables as `f32` in SPIR-V +2. Converting between `f16` and `f32` at runtime using `OpFConvert` +3. Maintaining mappings to track which variables need conversion +*/ + +use crate::back::spv::{Instruction, LocalType, NumericType, Word}; +use alloc::vec::Vec; + +/// Manages `f16` I/O polyfill state and operations. +#[derive(Default)] +pub(in crate::back::spv) struct F16IoPolyfill { + use_native: bool, + io_var_to_f32_type: crate::FastHashMap, +} + +impl F16IoPolyfill { + pub fn new(use_storage_input_output_16: bool) -> Self { + Self { + use_native: use_storage_input_output_16, + io_var_to_f32_type: crate::FastHashMap::default(), + } + } + + pub fn needs_polyfill(&self, ty_inner: &crate::TypeInner) -> bool { + use crate::{ScalarKind as Sk, TypeInner}; + + !self.use_native + && match *ty_inner { + TypeInner::Scalar(ref s) if s.kind == Sk::Float && s.width == 2 => true, + TypeInner::Vector { scalar, .. } + if scalar.kind == Sk::Float && scalar.width == 2 => + { + true + } + _ => false, + } + } + + pub fn register_io_var(&mut self, variable_id: Word, f32_type_id: Word) { + self.io_var_to_f32_type.insert(variable_id, f32_type_id); + } + + pub fn get_f32_io_type(&self, variable_id: Word) -> Option { + self.io_var_to_f32_type.get(&variable_id).copied() + } + + pub fn emit_f16_to_f32_conversion( + f16_value_id: Word, + f32_type_id: Word, + converted_id: Word, + body: &mut Vec, + ) { + body.push(Instruction::unary( + spirv::Op::FConvert, + f32_type_id, + converted_id, + f16_value_id, + )); + } + + pub fn emit_f32_to_f16_conversion( + f32_value_id: Word, + f16_type_id: Word, + converted_id: Word, + body: &mut Vec, + ) { + body.push(Instruction::unary( + spirv::Op::FConvert, + f16_type_id, + converted_id, + f32_value_id, + )); + } + + pub fn create_polyfill_type(ty_inner: &crate::TypeInner) -> Option { + use crate::{ScalarKind as Sk, TypeInner}; + + match *ty_inner { + TypeInner::Scalar(ref s) if s.kind == Sk::Float && s.width == 2 => { + Some(LocalType::Numeric(NumericType::Scalar(crate::Scalar::F32))) + } + TypeInner::Vector { size, scalar } if scalar.kind == Sk::Float && scalar.width == 2 => { + Some(LocalType::Numeric(NumericType::Vector { + size, + scalar: crate::Scalar::F32, + })) + } + _ => None, + } + } +} + +impl crate::back::spv::recyclable::Recyclable for F16IoPolyfill { + fn recycle(mut self) -> Self { + self.io_var_to_f32_type = self.io_var_to_f32_type.recycle(); + self + } +} diff --git a/naga/src/back/spv/helpers.rs b/naga/src/back/spv/helpers.rs index 7938791bae0..84e130efaa3 100644 --- a/naga/src/back/spv/helpers.rs +++ b/naga/src/back/spv/helpers.rs @@ -35,6 +35,9 @@ pub(super) fn string_to_byte_chunks(input: &str, limit: usize) -> Vec<&[u8]> { let mut words = vec![]; while offset < input.len() { offset = input.floor_char_boundary(offset + limit); + // Clippy wants us to call as_bytes() first to avoid the UTF-8 check, + // but we want to assert the output is valid UTF-8. + #[allow(clippy::sliced_string_as_bytes)] words.push(input[start..offset].as_bytes()); start = offset; } diff --git a/naga/src/back/spv/image.rs b/naga/src/back/spv/image.rs index f485014530e..3aec1333f0c 100644 --- a/naga/src/back/spv/image.rs +++ b/naga/src/back/spv/image.rs @@ -118,6 +118,7 @@ impl Load { crate::ImageClass::Depth { .. } | crate::ImageClass::Sampled { .. } => { spirv::Op::ImageFetch } + crate::ImageClass::External => unimplemented!(), }; // `OpImageRead` and `OpImageFetch` instructions produce vec4 @@ -303,7 +304,7 @@ impl BlockContext<'_> { return Err(Error::Validation("extending vec4 coordinate")); } ref other => { - log::error!("wrong coordinate type {:?}", other); + log::error!("wrong coordinate type {other:?}"); return Err(Error::Validation("coordinate type")); } }; diff --git a/naga/src/back/spv/index.rs b/naga/src/back/spv/index.rs index 45d628e0f71..3a15ee88060 100644 --- a/naga/src/back/spv/index.rs +++ b/naga/src/back/spv/index.rs @@ -277,7 +277,7 @@ impl BlockContext<'_> { Ok(MaybeKnown::Computed(length_id)) } Err(err) => { - log::error!("Sequence length for {:?} failed: {}", sequence, err); + log::error!("Sequence length for {sequence:?} failed: {err}"); Err(Error::Validation("indexable length")) } } diff --git a/naga/src/back/spv/mod.rs b/naga/src/back/spv/mod.rs index 2dcd95957d7..4690dc71951 100644 --- a/naga/src/back/spv/mod.rs +++ b/naga/src/back/spv/mod.rs @@ -5,6 +5,7 @@ Backend for [SPIR-V][spv] (Standard Portable Intermediate Representation). */ mod block; +mod f16_polyfill; mod helpers; mod image; mod index; @@ -25,7 +26,6 @@ use spirv::Word; use thiserror::Error; use crate::arena::{Handle, HandleVec}; -use crate::path_like::PathLikeRef; use crate::proc::{BoundsCheckPolicies, TypeResolution}; #[derive(Clone)] @@ -78,6 +78,8 @@ pub enum Error { Override, #[error(transparent)] ResolveArraySizeError(#[from] crate::proc::ResolveArraySizeError), + #[error("mapping of {0:?} is missing")] + MissingBinding(crate::ResourceBinding), } #[derive(Default)] @@ -93,7 +95,7 @@ impl IdGenerator { #[derive(Debug, Clone)] pub struct DebugInfo<'a> { pub source_code: &'a str, - pub file_name: PathLikeRef<'a>, + pub file_name: &'a str, pub language: SourceLanguage, } @@ -276,6 +278,7 @@ impl LocalImageType { flags: make_flags(false, ImageTypeFlags::empty()), image_format: format.into(), }, + crate::ImageClass::External => unimplemented!(), } } } @@ -744,6 +747,7 @@ pub struct Writer { bounds_check_policies: BoundsCheckPolicies, zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode, force_loop_bounding: bool, + use_storage_input_output_16: bool, void_type: Word, //TODO: convert most of these into vectors, addressable by handle indices lookup_type: crate::FastHashMap, @@ -757,6 +761,7 @@ pub struct Writer { constant_ids: HandleVec, cached_constants: crate::FastHashMap, global_variables: HandleVec, + fake_missing_bindings: bool, binding_map: BindingMap, // Cached expressions are only meaningful within a BlockContext, but we @@ -770,6 +775,10 @@ pub struct Writer { ray_get_committed_intersection_function: Option, ray_get_candidate_intersection_function: Option, + + /// F16 I/O polyfill manager for handling `f16` input/output variables + /// when `StorageInputOutput16` capability is not available. + io_f16_polyfills: f16_polyfill::F16IoPolyfill, } bitflags::bitflags! { @@ -804,10 +813,12 @@ bitflags::bitflags! { } } -#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)] +#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serialize", derive(serde::Serialize))] #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] pub struct BindingInfo { + pub descriptor_set: u32, + pub binding: u32, /// If the binding is an unsized binding array, this overrides the size. pub binding_array_size: Option, } @@ -832,6 +843,10 @@ pub struct Options<'a> { /// Configuration flags for the writer. pub flags: WriterFlags, + /// Don't panic on missing bindings. Instead use fake values for `Binding` + /// and `DescriptorSet` decorations. This may result in invalid SPIR-V. + pub fake_missing_bindings: bool, + /// Map of resources to information about the binding. pub binding_map: BindingMap, @@ -852,6 +867,10 @@ pub struct Options<'a> { /// to think the number of iterations is bounded. pub force_loop_bounding: bool, + /// Whether to use the `StorageInputOutput16` capability for `f16` shader I/O. + /// When false, `f16` I/O is polyfilled using `f32` types with conversions. + pub use_storage_input_output_16: bool, + pub debug_info: Option>, } @@ -866,11 +885,13 @@ impl Default for Options<'_> { Options { lang_version: (1, 0), flags, + fake_missing_bindings: true, binding_map: BindingMap::default(), capabilities: None, bounds_check_policies: BoundsCheckPolicies::default(), zero_initialize_workgroup_memory: ZeroInitializeWorkgroupMemoryMode::Polyfill, force_loop_bounding: true, + use_storage_input_output_16: true, debug_info: None, } } diff --git a/naga/src/back/spv/writer.rs b/naga/src/back/spv/writer.rs index b61747c8326..c86a53c6ef8 100644 --- a/naga/src/back/spv/writer.rs +++ b/naga/src/back/spv/writer.rs @@ -14,7 +14,6 @@ use super::{ use crate::{ arena::{Handle, HandleVec, UniqueArena}, back::spv::{BindingInfo, WrappedFunction}, - path_like::PathLike, proc::{Alignment, TypeResolution}, valid::{FunctionInfo, ModuleInfo}, }; @@ -78,6 +77,7 @@ impl Writer { bounds_check_policies: options.bounds_check_policies, zero_initialize_workgroup_memory: options.zero_initialize_workgroup_memory, force_loop_bounding: options.force_loop_bounding, + use_storage_input_output_16: options.use_storage_input_output_16, void_type, lookup_type: crate::FastHashMap::default(), lookup_function: crate::FastHashMap::default(), @@ -86,15 +86,37 @@ impl Writer { constant_ids: HandleVec::new(), cached_constants: crate::FastHashMap::default(), global_variables: HandleVec::new(), + fake_missing_bindings: options.fake_missing_bindings, binding_map: options.binding_map.clone(), saved_cached: CachedExpressions::default(), gl450_ext_inst_id, temp_list: Vec::new(), ray_get_committed_intersection_function: None, ray_get_candidate_intersection_function: None, + io_f16_polyfills: super::f16_polyfill::F16IoPolyfill::new( + options.use_storage_input_output_16, + ), }) } + pub fn set_options(&mut self, options: &Options) -> Result<(), Error> { + let (major, minor) = options.lang_version; + if major != 1 { + return Err(Error::UnsupportedVersion(major, minor)); + } + self.physical_layout = PhysicalLayout::new(major, minor); + self.capabilities_available = options.capabilities.clone(); + self.flags = options.flags; + self.bounds_check_policies = options.bounds_check_policies; + self.zero_initialize_workgroup_memory = options.zero_initialize_workgroup_memory; + self.force_loop_bounding = options.force_loop_bounding; + self.use_storage_input_output_16 = options.use_storage_input_output_16; + self.binding_map = options.binding_map.clone(); + self.io_f16_polyfills = + super::f16_polyfill::F16IoPolyfill::new(options.use_storage_input_output_16); + Ok(()) + } + /// Returns `(major, minor)` of the SPIR-V language version. pub const fn lang_version(&self) -> (u8, u8) { self.physical_layout.lang_version() @@ -125,7 +147,9 @@ impl Writer { bounds_check_policies: self.bounds_check_policies, zero_initialize_workgroup_memory: self.zero_initialize_workgroup_memory, force_loop_bounding: self.force_loop_bounding, + use_storage_input_output_16: self.use_storage_input_output_16, capabilities_available: take(&mut self.capabilities_available), + fake_missing_bindings: self.fake_missing_bindings, binding_map: take(&mut self.binding_map), // Initialized afresh: @@ -151,6 +175,7 @@ impl Writer { temp_list: take(&mut self.temp_list).recycle(), ray_get_candidate_intersection_function: None, ray_get_committed_intersection_function: None, + io_f16_polyfills: take(&mut self.io_f16_polyfills).recycle(), }; *self = fresh; @@ -445,6 +470,26 @@ impl Writer { }) } + /// Resolve the [`BindingInfo`] for a [`crate::ResourceBinding`] from the + /// provided [`Writer::binding_map`]. + /// + /// If the specified resource is not present in the binding map this will + /// return an error, unless [`Writer::fake_missing_bindings`] is set. + fn resolve_resource_binding( + &self, + res_binding: &crate::ResourceBinding, + ) -> Result { + match self.binding_map.get(res_binding) { + Some(target) => Ok(*target), + None if self.fake_missing_bindings => Ok(BindingInfo { + descriptor_set: res_binding.group, + binding: res_binding.binding, + binding_array_size: None, + }), + None => Err(Error::MissingBinding(*res_binding)), + } + } + /// Emits code for any wrapper functions required by the expressions in ir_function. /// The IDs of any emitted functions will be stored in [`Self::wrapped_functions`]. fn write_wrapped_functions( @@ -726,10 +771,11 @@ impl Writer { binding, )?; iface.varying_ids.push(varying_id); - let id = self.id_gen.next(); - prelude - .body - .push(Instruction::load(argument_type_id, id, varying_id, None)); + let id = self.load_io_with_f16_polyfill( + &mut prelude.body, + varying_id, + argument_type_id, + ); if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) { local_invocation_id = Some(id); @@ -754,13 +800,11 @@ impl Writer { binding, )?; iface.varying_ids.push(varying_id); - let id = self.id_gen.next(); - prelude - .body - .push(Instruction::load(type_id, id, varying_id, None)); + let id = + self.load_io_with_f16_polyfill(&mut prelude.body, varying_id, type_id); constituent_ids.push(id); - if binding == &crate::Binding::BuiltIn(crate::BuiltIn::GlobalInvocationId) { + if binding == &crate::Binding::BuiltIn(crate::BuiltIn::LocalInvocationId) { local_invocation_id = Some(id); } } @@ -1220,8 +1264,10 @@ impl Writer { .insert(spirv::Capability::StorageBuffer16BitAccess); self.capabilities_used .insert(spirv::Capability::UniformAndStorageBuffer16BitAccess); - self.capabilities_used - .insert(spirv::Capability::StorageInputOutput16); + if self.use_storage_input_output_16 { + self.capabilities_used + .insert(spirv::Capability::StorageInputOutput16); + } } Instruction::type_float(id, bits) } @@ -1246,6 +1292,7 @@ impl Writer { self.request_image_format_capabilities(format.into())?; false } + crate::ImageClass::External => unimplemented!(), }; match dim { @@ -1904,8 +1951,26 @@ impl Writer { ty: Handle, binding: &crate::Binding, ) -> Result { + use crate::TypeInner; + let id = self.id_gen.next(); - let pointer_type_id = self.get_handle_pointer_type_id(ty, class); + let ty_inner = &ir_module.types[ty].inner; + let needs_polyfill = self.needs_f16_polyfill(ty_inner); + + let pointer_type_id = if needs_polyfill { + let f32_value_local = + super::f16_polyfill::F16IoPolyfill::create_polyfill_type(ty_inner) + .expect("needs_polyfill returned true but create_polyfill_type returned None"); + + let f32_type_id = self.get_localtype_id(f32_value_local); + let ptr_id = self.get_pointer_type_id(f32_type_id, class); + self.io_f16_polyfills.register_io_var(id, f32_type_id); + + ptr_id + } else { + self.get_handle_pointer_type_id(ty, class) + }; + Instruction::variable(pointer_type_id, id, class, None) .to_words(&mut self.logical_layout.declarations); @@ -2088,8 +2153,9 @@ impl Writer { // > shader, must be decorated Flat if class == spirv::StorageClass::Input && stage == crate::ShaderStage::Fragment { let is_flat = match ir_module.types[ty].inner { - crate::TypeInner::Scalar(scalar) - | crate::TypeInner::Vector { scalar, .. } => match scalar.kind { + TypeInner::Scalar(scalar) | TypeInner::Vector { scalar, .. } => match scalar + .kind + { Sk::Uint | Sk::Sint | Sk::Bool => true, Sk::Float => false, Sk::AbstractInt | Sk::AbstractFloat => { @@ -2111,6 +2177,49 @@ impl Writer { Ok(id) } + /// Load an IO variable, converting from `f32` to `f16` if polyfill is active. + /// Returns the id of the loaded value matching `target_type_id`. + pub(super) fn load_io_with_f16_polyfill( + &mut self, + body: &mut Vec, + varying_id: Word, + target_type_id: Word, + ) -> Word { + let tmp = self.id_gen.next(); + if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) { + body.push(Instruction::load(f32_ty, tmp, varying_id, None)); + let converted = self.id_gen.next(); + super::f16_polyfill::F16IoPolyfill::emit_f32_to_f16_conversion( + tmp, + target_type_id, + converted, + body, + ); + converted + } else { + body.push(Instruction::load(target_type_id, tmp, varying_id, None)); + tmp + } + } + + /// Store an IO variable, converting from `f16` to `f32` if polyfill is active. + pub(super) fn store_io_with_f16_polyfill( + &mut self, + body: &mut Vec, + varying_id: Word, + value_id: Word, + ) { + if let Some(f32_ty) = self.io_f16_polyfills.get_f32_io_type(varying_id) { + let converted = self.id_gen.next(); + super::f16_polyfill::F16IoPolyfill::emit_f16_to_f32_conversion( + value_id, f32_ty, converted, body, + ); + body.push(Instruction::store(varying_id, converted, None)); + } else { + body.push(Instruction::store(varying_id, value_id, None)); + } + } + fn write_global_variable( &mut self, ir_module: &crate::Module, @@ -2153,13 +2262,11 @@ impl Writer { // and it is failing on 0. let mut substitute_inner_type_lookup = None; if let Some(ref res_binding) = global_variable.binding { - self.decorate(id, Decoration::DescriptorSet, &[res_binding.group]); - self.decorate(id, Decoration::Binding, &[res_binding.binding]); + let bind_target = self.resolve_resource_binding(res_binding)?; + self.decorate(id, Decoration::DescriptorSet, &[bind_target.descriptor_set]); + self.decorate(id, Decoration::Binding, &[bind_target.binding]); - if let Some(&BindingInfo { - binding_array_size: Some(remapped_binding_array_size), - }) = self.binding_map.get(res_binding) - { + if let Some(remapped_binding_array_size) = bind_target.binding_array_size { if let crate::TypeInner::BindingArray { base, .. } = ir_module.types[global_variable.ty].inner { @@ -2411,10 +2518,8 @@ impl Writer { if self.flags.contains(WriterFlags::DEBUG) { if let Some(debug_info) = debug_info.as_ref() { let source_file_id = self.id_gen.next(); - self.debugs.push(Instruction::string( - &debug_info.file_name.to_string_lossy(), - source_file_id, - )); + self.debugs + .push(Instruction::string(debug_info.file_name, source_file_id)); debug_info_inner = Some(DebugInfoInner { source_code: debug_info.source_code, @@ -2584,6 +2689,10 @@ impl Writer { self.decorate(id, spirv::Decoration::NonUniform, &[]); Ok(()) } + + pub(super) fn needs_f16_polyfill(&self, ty_inner: &crate::TypeInner) -> bool { + self.io_f16_polyfills.needs_polyfill(ty_inner) + } } #[test] diff --git a/naga/src/back/wgsl/writer.rs b/naga/src/back/wgsl/writer.rs index 58c1b734f09..225a63343bf 100644 --- a/naga/src/back/wgsl/writer.rs +++ b/naga/src/back/wgsl/writer.rs @@ -101,7 +101,7 @@ impl Writer { module, &crate::keywords::wgsl::RESERVED_SET, // an identifier must not start with two underscore - &[], + proc::CaseInsensitiveKeywordSet::empty(), &["__", "_naga"], &mut self.names, ); @@ -109,12 +109,29 @@ impl Writer { self.required_polyfills.clear(); } - fn is_builtin_wgsl_struct(&self, module: &Module, handle: Handle) -> bool { + /// Determine if `ty` is the Naga IR presentation of a WGSL builtin type. + /// + /// Return true if `ty` refers to the Naga IR form of a WGSL builtin type + /// like `__atomic_compare_exchange_result`. + /// + /// Even though the module may use the type, the WGSL backend should avoid + /// emitting a definition for it, since it is [predeclared] in WGSL. + /// + /// This also covers types like [`NagaExternalTextureParams`], which other + /// backends use to lower WGSL constructs like external textures to their + /// implementations. WGSL can express these directly, so the types need not + /// be emitted. + /// + /// [predeclared]: https://www.w3.org/TR/WGSL/#predeclared + /// [`NagaExternalTextureParams`]: crate::ir::SpecialTypes::external_texture_params + fn is_builtin_wgsl_struct(&self, module: &Module, ty: Handle) -> bool { module .special_types .predeclared_types .values() - .any(|t| *t == handle) + .any(|t| *t == ty) + || Some(ty) == module.special_types.external_texture_params + || Some(ty) == module.special_types.external_texture_transfer_function } pub fn write(&mut self, module: &Module, info: &valid::ModuleInfo) -> BackendResult { diff --git a/naga/src/common/wgsl/to_wgsl.rs b/naga/src/common/wgsl/to_wgsl.rs index 035c4eafb32..72be441288f 100644 --- a/naga/src/common/wgsl/to_wgsl.rs +++ b/naga/src/common/wgsl/to_wgsl.rs @@ -244,7 +244,7 @@ impl ToWgsl for crate::StorageFormat { Sf::Bgra8Unorm => "bgra8unorm", Sf::Rgb10a2Uint => "rgb10a2uint", Sf::Rgb10a2Unorm => "rgb10a2unorm", - Sf::Rg11b10Ufloat => "rg11b10float", + Sf::Rg11b10Ufloat => "rg11b10ufloat", Sf::R64Uint => "r64uint", Sf::Rg32Uint => "rg32uint", Sf::Rg32Sint => "rg32sint", diff --git a/naga/src/common/wgsl/types.rs b/naga/src/common/wgsl/types.rs index c118feeace7..82b8eeaa67a 100644 --- a/naga/src/common/wgsl/types.rs +++ b/naga/src/common/wgsl/types.rs @@ -250,6 +250,9 @@ where "texture_storage_{dim_str}{arrayed_str}<{format_str}{access_str}>" )?; } + Ic::External => { + write!(out, "texture_external")?; + } } } TypeInner::Scalar(scalar) => { @@ -337,7 +340,7 @@ where } => { let (address, maybe_access) = address_space_str(space); if let Some(space) = address { - write!(out, "ptr<{}, ", space)?; + write!(out, "ptr<{space}, ")?; ctx.write_scalar(scalar, out)?; if let Some(access) = maybe_access { write!(out, ", {access}")?; @@ -368,14 +371,14 @@ where } TypeInner::AccelerationStructure { vertex_return } => { let caps = if vertex_return { "" } else { "" }; - write!(out, "acceleration_structure{}", caps)? + write!(out, "acceleration_structure{caps}")? } TypeInner::Struct { .. } => { ctx.write_unnamed_struct(inner, out)?; } TypeInner::RayQuery { vertex_return } => { let caps = if vertex_return { "" } else { "" }; - write!(out, "ray_query{}", caps)? + write!(out, "ray_query{caps}")? } } diff --git a/naga/src/compact/expressions.rs b/naga/src/compact/expressions.rs index 24562bdd4af..f36d747a935 100644 --- a/naga/src/compact/expressions.rs +++ b/naga/src/compact/expressions.rs @@ -68,7 +68,7 @@ impl ExpressionTracer<'_> { continue; } - log::trace!("tracing new expression {:?}", expr); + log::trace!("tracing new expression {expr:?}"); self.trace_expression(expr); } } diff --git a/naga/src/compact/functions.rs b/naga/src/compact/functions.rs index eb829aec80e..dc91cade341 100644 --- a/naga/src/compact/functions.rs +++ b/naga/src/compact/functions.rs @@ -56,7 +56,7 @@ impl FunctionTracer<'_> { self.as_expression().trace_expressions(); } - fn as_expression(&mut self) -> super::expressions::ExpressionTracer { + fn as_expression(&mut self) -> super::expressions::ExpressionTracer<'_> { super::expressions::ExpressionTracer { constants: self.constants, overrides: self.overrides, diff --git a/naga/src/compact/handle_set_map.rs b/naga/src/compact/handle_set_map.rs index 7a174cd9171..b3cbb909eed 100644 --- a/naga/src/compact/handle_set_map.rs +++ b/naga/src/compact/handle_set_map.rs @@ -37,7 +37,7 @@ impl HandleMap { if self.new_index.len() <= handle.index() { self.new_index.resize_with(handle.index() + 1, || None); } - core::mem::replace(&mut self.new_index[handle.index()], Some(value)) + self.new_index[handle.index()].replace(value) } } diff --git a/naga/src/compact/mod.rs b/naga/src/compact/mod.rs index c7b7bfb46d8..d059ba21e4f 100644 --- a/naga/src/compact/mod.rs +++ b/naga/src/compact/mod.rs @@ -164,7 +164,7 @@ pub fn compact(module: &mut crate::Module, keep_unused: KeepUnused) { } while let Some(handle) = module_tracer.functions_pending.pop() { let function = &module.functions[handle]; - log::trace!("tracing function {:?}", function); + log::trace!("tracing function {function:?}"); let mut function_tracer = module_tracer.as_function(function); function_tracer.trace(); function_maps.insert(handle, FunctionMap::from(function_tracer)); @@ -380,6 +380,8 @@ impl<'module> ModuleTracer<'module> { ref ray_intersection, ref ray_vertex_return, ref predeclared_types, + ref external_texture_params, + ref external_texture_transfer_function, } = *special_types; if let Some(ray_desc) = *ray_desc { @@ -391,6 +393,15 @@ impl<'module> ModuleTracer<'module> { if let Some(ray_vertex_return) = *ray_vertex_return { self.types_used.insert(ray_vertex_return); } + // The `external_texture_params` type is generated purely as a + // convenience to the backends. While it will never actually be used in + // the IR, it must be marked as used so that it survives compaction. + if let Some(external_texture_params) = *external_texture_params { + self.types_used.insert(external_texture_params); + } + if let Some(external_texture_transfer_function) = *external_texture_transfer_function { + self.types_used.insert(external_texture_transfer_function); + } for (_, &handle) in predeclared_types { self.types_used.insert(handle); } @@ -460,7 +471,7 @@ impl<'module> ModuleTracer<'module> { } } - fn as_type(&mut self) -> types::TypeTracer { + fn as_type(&mut self) -> types::TypeTracer<'_> { types::TypeTracer { overrides: &self.module.overrides, types_used: &mut self.types_used, @@ -469,7 +480,7 @@ impl<'module> ModuleTracer<'module> { } } - fn as_const_expression(&mut self) -> expressions::ExpressionTracer { + fn as_const_expression(&mut self) -> expressions::ExpressionTracer<'_> { expressions::ExpressionTracer { constants: &self.module.constants, overrides: &self.module.overrides, @@ -532,6 +543,8 @@ impl ModuleMap { ref mut ray_intersection, ref mut ray_vertex_return, ref mut predeclared_types, + ref mut external_texture_params, + ref mut external_texture_transfer_function, } = *special; if let Some(ref mut ray_desc) = *ray_desc { @@ -545,6 +558,16 @@ impl ModuleMap { self.types.adjust(ray_vertex_return); } + if let Some(ref mut external_texture_params) = *external_texture_params { + self.types.adjust(external_texture_params); + } + + if let Some(ref mut external_texture_transfer_function) = + *external_texture_transfer_function + { + self.types.adjust(external_texture_transfer_function); + } + for handle in predeclared_types.values_mut() { self.types.adjust(handle); } @@ -680,7 +703,7 @@ fn type_expression_interdependence() { }; let mut type_name_counter = 0; let mut type_needed = |module: &mut crate::Module, handle| { - let name = Some(format!("type{}", type_name_counter)); + let name = Some(format!("type{type_name_counter}")); type_name_counter += 1; module.types.insert( crate::Type { @@ -696,7 +719,7 @@ fn type_expression_interdependence() { }; let mut override_name_counter = 0; let mut expression_needed = |module: &mut crate::Module, handle| { - let name = Some(format!("override{}", override_name_counter)); + let name = Some(format!("override{override_name_counter}")); override_name_counter += 1; module.overrides.append( crate::Override { diff --git a/naga/src/error.rs b/naga/src/error.rs index 597aad99202..282cb6c88c5 100644 --- a/naga/src/error.rs +++ b/naga/src/error.rs @@ -1,4 +1,4 @@ -use alloc::{boxed::Box, string::String}; +use alloc::{borrow::Cow, boxed::Box, string::String}; use core::{error::Error, fmt}; #[derive(Clone, Debug)] @@ -41,7 +41,7 @@ impl fmt::Display for ShaderError use codespan_reporting::{files::SimpleFile, term}; let label = self.label.as_deref().unwrap_or_default(); - let files = SimpleFile::new(label, &self.source); + let files = SimpleFile::new(label, replace_control_chars(&self.source)); let config = term::Config::default(); let writer = { @@ -56,7 +56,7 @@ impl fmt::Display for ShaderError writer.into_string() }; - write!(f, "\nShader validation {}", writer) + write!(f, "\nShader validation {writer}") } } @@ -134,6 +134,35 @@ where E: Error + 'static, { fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.inner) + self.inner.source() } } + +pub(crate) fn replace_control_chars(s: &str) -> Cow<'_, str> { + const REPLACEMENT_CHAR: &str = "\u{FFFD}"; + debug_assert_eq!( + REPLACEMENT_CHAR.chars().next().unwrap(), + char::REPLACEMENT_CHARACTER + ); + + let mut res = Cow::Borrowed(s); + let mut offset = 0; + + while let Some(found_pos) = res[offset..].find(|c: char| c.is_control() && !c.is_whitespace()) { + offset += found_pos; + let found_len = res[offset..].chars().next().unwrap().len_utf8(); + res.to_mut() + .replace_range(offset..offset + found_len, REPLACEMENT_CHAR); + offset += REPLACEMENT_CHAR.len(); + } + + res +} + +#[test] +fn test_replace_control_chars() { + // The UTF-8 encoding of \u{0080} is multiple bytes. + let input = "Foo\u{0080}Bar\u{0001}Baz\n"; + let expected = "Foo\u{FFFD}Bar\u{FFFD}Baz\n"; + assert_eq!(replace_control_chars(input), expected); +} diff --git a/naga/src/front/glsl/builtins.rs b/naga/src/front/glsl/builtins.rs index 6dcddda44e7..3d7588d27ba 100644 --- a/naga/src/front/glsl/builtins.rs +++ b/naga/src/front/glsl/builtins.rs @@ -2138,6 +2138,7 @@ impl Frontend { ImageClass::Depth { .. } => (true, false), ImageClass::Storage { .. } => (false, true), ImageClass::Sampled { .. } => (false, false), + ImageClass::External => unreachable!(), }; let coordinate = match (image_size, coord_size) { @@ -2259,6 +2260,7 @@ pub fn sampled_to_depth( kind: ErrorKind::SemanticError("Not a texture".into()), meta, }), + ImageClass::External => unreachable!(), }, _ => errors.push(Error { kind: ErrorKind::SemanticError("Not a texture".into()), diff --git a/naga/src/front/glsl/context.rs b/naga/src/front/glsl/context.rs index e6c5546fb95..d650044992e 100644 --- a/naga/src/front/glsl/context.rs +++ b/naga/src/front/glsl/context.rs @@ -541,7 +541,7 @@ impl<'a> Context<'a> { ) -> Result<(Option>, Span)> { let HirExpr { ref kind, meta } = stmt.hir_exprs[expr]; - log::debug!("Lowering {:?} (kind {:?}, pos {:?})", expr, kind, pos); + log::debug!("Lowering {expr:?} (kind {kind:?}, pos {pos:?})"); let handle = match *kind { HirExprKind::Access { base, index } => { @@ -636,8 +636,7 @@ impl<'a> Context<'a> { frontend.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "Cannot apply operation to {:?} and {:?}", - left_inner, right_inner + "Cannot apply operation to {left_inner:?} and {right_inner:?}" ) .into(), ), @@ -835,8 +834,7 @@ impl<'a> Context<'a> { frontend.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "Cannot apply operation to {:?} and {:?}", - left_inner, right_inner + "Cannot apply operation to {left_inner:?} and {right_inner:?}" ) .into(), ), @@ -916,8 +914,7 @@ impl<'a> Context<'a> { frontend.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "Cannot apply operation to {:?} and {:?}", - left_inner, right_inner + "Cannot apply operation to {left_inner:?} and {right_inner:?}" ) .into(), ), @@ -1339,13 +1336,7 @@ impl<'a> Context<'a> { } }; - log::trace!( - "Lowered {:?}\n\tKind = {:?}\n\tPos = {:?}\n\tResult = {:?}", - expr, - kind, - pos, - handle - ); + log::trace!("Lowered {expr:?}\n\tKind = {kind:?}\n\tPos = {pos:?}\n\tResult = {handle:?}"); Ok((Some(handle), meta)) } diff --git a/naga/src/front/glsl/error.rs b/naga/src/front/glsl/error.rs index 900ad52359d..9c9e58549fc 100644 --- a/naga/src/front/glsl/error.rs +++ b/naga/src/front/glsl/error.rs @@ -12,7 +12,7 @@ use pp_rs::token::PreprocessorError; use thiserror::Error; use super::token::TokenValue; -use crate::SourceLocation; +use crate::{error::replace_control_chars, SourceLocation}; use crate::{error::ErrorWrite, proc::ConstantEvaluatorError, Span}; fn join_with_comma(list: &[ExpectedToken]) -> String { @@ -171,7 +171,7 @@ impl ParseErrors { pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) { let path = path.to_string(); - let files = SimpleFile::new(path, source); + let files = SimpleFile::new(path, replace_control_chars(source)); let config = term::Config::default(); for err in &self.errors { @@ -198,11 +198,7 @@ impl core::fmt::Display for ParseErrors { } } -impl core::error::Error for ParseErrors { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - None - } -} +impl core::error::Error for ParseErrors {} impl From> for ParseErrors { fn from(errors: Vec) -> Self { diff --git a/naga/src/front/glsl/functions.rs b/naga/src/front/glsl/functions.rs index e0a0535a04e..7de7364cd40 100644 --- a/naga/src/front/glsl/functions.rs +++ b/naga/src/front/glsl/functions.rs @@ -560,7 +560,7 @@ impl Frontend { continue; } - log::trace!("Testing overload {}", overload_idx); + log::trace!("Testing overload {overload_idx}"); // Stores whether the current overload matches exactly the function call let mut exact = true; @@ -592,10 +592,7 @@ impl Frontend { let call_arg_ty = ctx.get_type(call_argument.0); log::trace!( - "Testing parameter {}\n\tOverload = {:?}\n\tCall = {:?}", - i, - overload_param_ty, - call_arg_ty + "Testing parameter {i}\n\tOverload = {overload_param_ty:?}\n\tCall = {call_arg_ty:?}" ); // Storage images cannot be directly compared since while the access is part of the @@ -641,8 +638,7 @@ impl Frontend { self.errors.push(Error { kind: ErrorKind::SemanticError( format!( - "'{}': image needs {:?} access but only {:?} was provided", - name, overload_access, call_access + "'{name}': image needs {overload_access:?} access but only {call_access:?} was provided" ) .into(), ), diff --git a/naga/src/front/mod.rs b/naga/src/front/mod.rs index 4f363d8973d..826100e878d 100644 --- a/naga/src/front/mod.rs +++ b/naga/src/front/mod.rs @@ -114,7 +114,7 @@ impl Typifier { for (eh, expr) in expressions.iter().skip(self.resolutions.len()) { //Note: the closure can't `Err` by construction let resolution = ctx.resolve(expr, |h| Ok(&self.resolutions[h]))?; - log::debug!("Resolving {:?} = {:?} : {:?}", eh, expr, resolution); + log::debug!("Resolving {eh:?} = {expr:?} : {resolution:?}"); self.resolutions.insert(eh, resolution); } } diff --git a/naga/src/front/spv/error.rs b/naga/src/front/spv/error.rs index acfda0a22fa..cf456a9db90 100644 --- a/naga/src/front/spv/error.rs +++ b/naga/src/front/spv/error.rs @@ -8,7 +8,11 @@ use codespan_reporting::files::SimpleFile; use codespan_reporting::term; use super::ModuleState; -use crate::{arena::Handle, error::ErrorWrite, front::atomic_upgrade}; +use crate::{ + arena::Handle, + error::{replace_control_chars, ErrorWrite}, + front::atomic_upgrade, +}; #[derive(Clone, Debug, thiserror::Error)] pub enum Error { @@ -157,7 +161,7 @@ impl Error { pub fn emit_to_writer_with_path(&self, writer: &mut impl ErrorWrite, source: &str, path: &str) { let path = path.to_string(); - let files = SimpleFile::new(path, source); + let files = SimpleFile::new(path, replace_control_chars(source)); let config = term::Config::default(); let diagnostic = Diagnostic::error().with_message(format!("{self:?}")); diff --git a/naga/src/front/spv/function.rs b/naga/src/front/spv/function.rs index 13163f6bd41..67cbf05f04f 100644 --- a/naga/src/front/spv/function.rs +++ b/naga/src/front/spv/function.rs @@ -167,7 +167,7 @@ impl> super::Frontend { if let Some(ref prefix) = self.options.block_ctx_dump_prefix { let dump_suffix = match self.lookup_entry_point.get(&fun_id) { Some(ep) => format!("block_ctx.{:?}-{}.txt", ep.stage, ep.name), - None => format!("block_ctx.Fun-{}.txt", function_index), + None => format!("block_ctx.Fun-{function_index}.txt"), }; cfg_if::cfg_if! { @@ -176,10 +176,10 @@ impl> super::Frontend { let dest = prefix.join(dump_suffix); let dump = format!("{block_ctx:#?}"); if let Err(e) = std::fs::write(&dest, dump) { - log::error!("Unable to dump the block context into {:?}: {}", dest, e); + log::error!("Unable to dump the block context into {dest:?}: {e}"); } } else { - log::error!("Unable to dump the block context into {:?}/{}: file system integration was not enabled with the `fs` feature", prefix, dump_suffix); + log::error!("Unable to dump the block context into {prefix:?}/{dump_suffix}: file system integration was not enabled with the `fs` feature"); } } } @@ -191,7 +191,7 @@ impl> super::Frontend { // to get the spill. for phi in block_ctx.phis.iter() { // Get a pointer to the local variable for the phi's value. - let phi_pointer = block_ctx.expressions.append( + let phi_pointer: Handle = block_ctx.expressions.append( crate::Expression::LocalVariable(phi.local), crate::Span::default(), ); @@ -603,7 +603,7 @@ impl> super::Frontend { } impl BlockContext<'_> { - pub(super) fn gctx(&self) -> crate::proc::GlobalCtx { + pub(super) fn gctx(&self) -> crate::proc::GlobalCtx<'_> { crate::proc::GlobalCtx { types: &self.module.types, constants: &self.module.constants, diff --git a/naga/src/front/spv/image.rs b/naga/src/front/spv/image.rs index 15dafa5af04..591590be024 100644 --- a/naga/src/front/spv/image.rs +++ b/naga/src/front/spv/image.rs @@ -48,6 +48,8 @@ pub struct SamplingOptions { pub project: bool, /// Depth comparison sampling with a reference value. pub compare: bool, + /// Gather sampling: Operates on four samples of one channel. + pub gather: bool, } enum ExtraCoordinate { @@ -202,7 +204,7 @@ pub(super) fn patch_comparison_type( return false; } - log::debug!("Flipping comparison for {:?}", var); + log::debug!("Flipping comparison for {var:?}"); let original_ty = &arena[var.ty]; let original_ty_span = arena.get_span(var.ty); let ty_inner = match original_ty.inner { @@ -279,7 +281,7 @@ impl> super::Frontend { if image_ops != 0 { let other = spirv::ImageOperands::from_bits_truncate(image_ops); - log::warn!("Unknown image write ops {:?}", other); + log::warn!("Unknown image write ops {other:?}"); for _ in 1..words_left { self.next()?; } @@ -403,7 +405,7 @@ impl> super::Frontend { words_left -= 1; } other => { - log::warn!("Unknown image load op {:?}", other); + log::warn!("Unknown image load op {other:?}"); for _ in 0..words_left { self.next()?; } @@ -500,10 +502,10 @@ impl> super::Frontend { let result_id = self.next()?; let sampled_image_id = self.next()?; let coordinate_id = self.next()?; - let dref_id = if options.compare { - Some(self.next()?) - } else { - None + let (component_id, dref_id) = match (options.gather, options.compare) { + (true, false) => (Some(self.next()?), None), + (_, true) => (None, Some(self.next()?)), + (_, _) => (None, None), }; let span = self.span_from_with_op(start); @@ -546,12 +548,11 @@ impl> super::Frontend { }; level = if options.compare { - log::debug!("Assuming {:?} is zero", lod_handle); + log::debug!("Assuming {lod_handle:?} is zero"); crate::SampleLevel::Zero } else if is_depth_image { log::debug!( - "Assuming level {:?} converts losslessly to an integer", - lod_handle + "Assuming level {lod_handle:?} converts losslessly to an integer" ); let expr = crate::Expression::As { expr: lod_handle, @@ -588,9 +589,7 @@ impl> super::Frontend { ); level = if options.compare { log::debug!( - "Assuming gradients {:?} and {:?} are not greater than 1", - grad_x_handle, - grad_y_handle + "Assuming gradients {grad_x_handle:?} and {grad_y_handle:?} are not greater than 1" ); crate::SampleLevel::Zero } else { @@ -616,7 +615,7 @@ impl> super::Frontend { words_left -= 1; } other => { - log::warn!("Unknown image sample operand {:?}", other); + log::warn!("Unknown image sample operand {other:?}"); for _ in 0..words_left { self.next()?; } @@ -632,6 +631,58 @@ impl> super::Frontend { self.get_expr_handle(coordinate_id, coord_lexp, ctx, emitter, block, body_idx); let coord_type_handle = self.lookup_type.lookup(coord_lexp.type_id)?.handle; + let gather = match (options.gather, component_id) { + (true, Some(component_id)) => { + let component_lexp = self.lookup_expression.lookup(component_id)?; + + let component_value = match ctx.expressions[component_lexp.handle] { + // VUID-StandaloneSpirv-OpImageGather-04664: + // The “Component” operand of OpImageGather, and OpImageSparseGather must be the + // of a constant instruction. + crate::Expression::Constant(const_handle) => { + let constant = &ctx.module.constants[const_handle]; + match ctx.module.global_expressions[constant.init] { + // SPIR-V specification: "It must be a 32-bit integer type scalar." + crate::Expression::Literal(crate::Literal::U32(value)) => value, + crate::Expression::Literal(crate::Literal::I32(value)) => value as u32, + _ => { + log::error!( + "Image gather component constant must be a 32-bit integer literal" + ); + return Err(Error::InvalidOperand); + } + } + } + _ => { + log::error!("Image gather component must be a constant"); + return Err(Error::InvalidOperand); + } + }; + + debug_assert_eq!(level, crate::SampleLevel::Auto); + level = crate::SampleLevel::Zero; + + // SPIR-V specification: "Behavior is undefined if its value is not 0, 1, 2 or 3." + match component_value { + 0 => Some(crate::SwizzleComponent::X), + 1 => Some(crate::SwizzleComponent::Y), + 2 => Some(crate::SwizzleComponent::Z), + 3 => Some(crate::SwizzleComponent::W), + other => { + log::error!("Invalid gather component operand: {other}"); + return Err(Error::InvalidOperand); + } + } + } + (true, None) => { + debug_assert_eq!(level, crate::SampleLevel::Auto); + level = crate::SampleLevel::Zero; + + Some(crate::SwizzleComponent::X) + } + (_, _) => None, + }; + let sampling_bit = if options.compare { SamplingFlags::COMPARISON } else { @@ -748,7 +799,7 @@ impl> super::Frontend { let expr = crate::Expression::ImageSample { image: si_lexp.image, sampler: si_lexp.sampler, - gather: None, //TODO + gather, coordinate, array_index, offset, diff --git a/naga/src/front/spv/mod.rs b/naga/src/front/spv/mod.rs index 2375e61c531..f54c8269f0f 100644 --- a/naga/src/front/spv/mod.rs +++ b/naga/src/front/spv/mod.rs @@ -44,7 +44,6 @@ use petgraph::graphmap::GraphMap; use super::atomic_upgrade::Upgrades; use crate::{ arena::{Arena, Handle, UniqueArena}, - path_like::PathLikeOwned, proc::{Alignment, Layouter}, FastHashMap, FastHashSet, FastIndexMap, }; @@ -82,6 +81,8 @@ pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[ spirv::Capability::GroupNonUniformBallot, spirv::Capability::GroupNonUniformShuffle, spirv::Capability::GroupNonUniformShuffleRelative, + spirv::Capability::RuntimeDescriptorArray, + spirv::Capability::StorageImageMultisample, // tricky ones spirv::Capability::UniformBufferArrayDynamicIndexing, spirv::Capability::StorageBufferArrayDynamicIndexing, @@ -90,6 +91,7 @@ pub const SUPPORTED_EXTENSIONS: &[&str] = &[ "SPV_KHR_storage_buffer_storage_class", "SPV_KHR_vulkan_memory_model", "SPV_KHR_multiview", + "SPV_EXT_descriptor_indexing", "SPV_EXT_shader_atomic_float_add", "SPV_KHR_16bit_storage", ]; @@ -381,7 +383,7 @@ pub struct Options { pub adjust_coordinate_space: bool, /// Only allow shaders with the known set of capabilities. pub strict_capabilities: bool, - pub block_ctx_dump_prefix: Option, + pub block_ctx_dump_prefix: Option, } impl Default for Options { @@ -515,7 +517,6 @@ enum MergeBlockInformation { /// [`blocks`]: BlockContext::blocks /// [`bodies`]: BlockContext::bodies /// [`phis`]: BlockContext::phis -/// [`lower`]: function::lower #[derive(Debug)] struct BlockContext<'function> { /// Phi nodes encountered when parsing the function, used to generate spills @@ -796,7 +797,7 @@ impl> Frontend { dec.specialization_constant_id = Some(self.next()?); } other => { - log::warn!("Unknown decoration {:?}", other); + log::warn!("Unknown decoration {other:?}"); for _ in base_words + 1..inst.wc { let _var = self.next()?; } @@ -1387,7 +1388,7 @@ impl> Frontend { block: &mut crate::Block, body_idx: usize, ) -> Result<(Handle, Handle), Error> { - log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id); + log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}"); let p_lexp_handle; let p_lexp_ty_id; { @@ -1600,7 +1601,7 @@ impl> Frontend { .remove(&result_id) .and_then(|decor| decor.name); if let Some(ref name) = name { - log::debug!("\t\t\tid={} name={}", result_id, name); + log::debug!("\t\t\tid={result_id} name={name}"); } let lookup_ty = self.lookup_type.lookup(result_type_id)?; let var_handle = ctx.local_arena.append( @@ -1686,7 +1687,7 @@ impl> Frontend { let result_type_id = self.next()?; let result_id = self.next()?; let base_id = self.next()?; - log::trace!("\t\t\tlooking up expr {:?}", base_id); + log::trace!("\t\t\tlooking up expr {base_id:?}"); let mut acex = { let lexp = self.lookup_expression.lookup(base_id)?; @@ -1721,7 +1722,7 @@ impl> Frontend { for _ in 4..inst.wc { let access_id = self.next()?; - log::trace!("\t\t\tlooking up index expr {:?}", access_id); + log::trace!("\t\t\tlooking up index expr {access_id:?}"); let index_expr = self.lookup_expression.lookup(access_id)?.clone(); let index_expr_handle = get_expr_handle!(access_id, &index_expr); let index_expr_data = &ctx.expressions[index_expr.handle]; @@ -2064,7 +2065,7 @@ impl> Frontend { let result_type_id = self.next()?; let result_id = self.next()?; let base_id = self.next()?; - log::trace!("\t\t\tlooking up expr {:?}", base_id); + log::trace!("\t\t\tlooking up expr {base_id:?}"); let mut lexp = self.lookup_expression.lookup(base_id)?.clone(); lexp.handle = get_expr_handle!(base_id, &lexp); for _ in 4..inst.wc { @@ -2084,7 +2085,7 @@ impl> Frontend { .base_id .ok_or(Error::InvalidAccessType(lexp.type_id))?, ref other => { - log::warn!("composite type {:?}", other); + log::warn!("composite type {other:?}"); return Err(Error::UnsupportedType(type_lookup.handle)); } }; @@ -2153,7 +2154,7 @@ impl> Frontend { let mut components = Vec::with_capacity(inst.wc as usize - 2); for _ in 3..inst.wc { let comp_id = self.next()?; - log::trace!("\t\t\tlooking up expr {:?}", comp_id); + log::trace!("\t\t\tlooking up expr {comp_id:?}"); let lexp = self.lookup_expression.lookup(comp_id)?; let handle = get_expr_handle!(comp_id, lexp); components.push(handle); @@ -2800,6 +2801,7 @@ impl> Frontend { let options = image::SamplingOptions { compare: false, project: false, + gather: false, }; self.parse_image_sample( extra, @@ -2816,6 +2818,7 @@ impl> Frontend { let options = image::SamplingOptions { compare: false, project: true, + gather: false, }; self.parse_image_sample( extra, @@ -2832,6 +2835,7 @@ impl> Frontend { let options = image::SamplingOptions { compare: true, project: false, + gather: false, }; self.parse_image_sample( extra, @@ -2848,6 +2852,41 @@ impl> Frontend { let options = image::SamplingOptions { compare: true, project: true, + gather: false, + }; + self.parse_image_sample( + extra, + options, + ctx, + &mut emitter, + &mut block, + block_id, + body_idx, + )?; + } + Op::ImageGather => { + let extra = inst.expect_at_least(6)?; + let options = image::SamplingOptions { + compare: false, + project: false, + gather: true, + }; + self.parse_image_sample( + extra, + options, + ctx, + &mut emitter, + &mut block, + block_id, + body_idx, + )?; + } + Op::ImageDrefGather => { + let extra = inst.expect_at_least(6)?; + let options = image::SamplingOptions { + compare: true, + project: false, + gather: true, }; self.parse_image_sample( extra, @@ -3870,9 +3909,12 @@ impl> Frontend { crate::Barrier::TEXTURE, semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0, ); + + block.extend(emitter.finish(ctx.expressions)); block.push(crate::Statement::ControlBarrier(flags), span); + emitter.start(ctx.expressions); } else { - log::warn!("Unsupported barrier execution scope: {}", exec_scope); + log::warn!("Unsupported barrier execution scope: {exec_scope}"); } } Op::MemoryBarrier => { @@ -3912,7 +3954,10 @@ impl> Frontend { crate::Barrier::TEXTURE, semantics & spirv::MemorySemantics::IMAGE_MEMORY.bits() != 0, ); + + block.extend(emitter.finish(ctx.expressions)); block.push(crate::Statement::MemoryBarrier(flags), span); + emitter.start(ctx.expressions); } Op::CopyObject => { inst.expect(4)?; @@ -4244,7 +4289,7 @@ impl> Frontend { let _memory_semantics_id = self.next()?; let span = self.span_from_with_op(start); - log::trace!("\t\t\tlooking up expr {:?}", pointer_id); + log::trace!("\t\t\tlooking up expr {pointer_id:?}"); let p_lexp_handle = get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?); @@ -4274,11 +4319,11 @@ impl> Frontend { let value_id = self.next()?; let span = self.span_from_with_op(start); - log::trace!("\t\t\tlooking up pointer expr {:?}", pointer_id); + log::trace!("\t\t\tlooking up pointer expr {pointer_id:?}"); let p_lexp_handle = get_expr_handle!(pointer_id, self.lookup_expression.lookup(pointer_id)?); - log::trace!("\t\t\tlooking up value expr {:?}", pointer_id); + log::trace!("\t\t\tlooking up value expr {pointer_id:?}"); let v_lexp_handle = get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?); @@ -4380,11 +4425,11 @@ impl> Frontend { body_idx, )?; - log::trace!("\t\t\tlooking up value expr {:?}", value_id); + log::trace!("\t\t\tlooking up value expr {value_id:?}"); let v_lexp_handle = get_expr_handle!(value_id, self.lookup_expression.lookup(value_id)?); - log::trace!("\t\t\tlooking up comparator expr {:?}", value_id); + log::trace!("\t\t\tlooking up comparator expr {value_id:?}"); let c_lexp_handle = get_expr_handle!( comparator_id, self.lookup_expression.lookup(comparator_id)? @@ -4699,7 +4744,7 @@ impl> Frontend { let generator = self.next()?; let _bound = self.next()?; let _schema = self.next()?; - log::info!("Generated by {} version {:x}", generator, version_raw); + log::info!("Generated by {generator} version {version_raw:x}"); crate::Module::default() }; @@ -4840,7 +4885,7 @@ impl> Frontend { if self.options.strict_capabilities { return Err(Error::UnsupportedCapability(cap)); } else { - log::warn!("Unknown capability {:?}", cap); + log::warn!("Unknown capability {cap:?}"); } } Ok(()) @@ -6030,7 +6075,7 @@ impl> Frontend { ) { Ok(handle) => Some(handle), Err(e) => { - log::warn!("Failed to initialize output built-in: {}", e); + log::warn!("Failed to initialize output built-in: {e}"); None } } @@ -6077,7 +6122,7 @@ impl> Frontend { let handle = module.global_variables.append(var, span); if module.types[ty].inner.can_comparison_sample(module) { - log::debug!("\t\ttracking {:?} for sampling properties", handle); + log::debug!("\t\ttracking {handle:?} for sampling properties"); self.handle_sampling .insert(handle, image::SamplingFlags::empty()); diff --git a/naga/src/front/type_gen.rs b/naga/src/front/type_gen.rs index 9a01b637d5a..4165f051db0 100644 --- a/naga/src/front/type_gen.rs +++ b/naga/src/front/type_gen.rs @@ -276,6 +276,179 @@ impl crate::Module { handle } + /// Generate [`SpecialTypes::external_texture_params`] and + /// [`SpecialTypes::external_texture_transfer_function`]. + /// + /// Other than the WGSL backend, every backend that supports external + /// textures does so by lowering them to a set of ordinary textures and + /// some parameters saying how to sample from them. These types are used + /// for said parameters. Note that they are not used by the IR, but + /// generated purely as a convenience for the backends. + /// + /// [`SpecialTypes::external_texture_params`]: crate::ir::SpecialTypes::external_texture_params + /// [`SpecialTypes::external_texture_transfer_function`]: crate::ir::SpecialTypes::external_texture_transfer_function + pub fn generate_external_texture_types(&mut self) { + if self.special_types.external_texture_params.is_some() { + return; + } + + let ty_f32 = self.types.insert( + crate::Type { + name: None, + inner: crate::TypeInner::Scalar(crate::Scalar::F32), + }, + Span::UNDEFINED, + ); + let ty_u32 = self.types.insert( + crate::Type { + name: None, + inner: crate::TypeInner::Scalar(crate::Scalar::U32), + }, + Span::UNDEFINED, + ); + let ty_vec2u = self.types.insert( + crate::Type { + name: None, + inner: crate::TypeInner::Vector { + size: crate::VectorSize::Bi, + scalar: crate::Scalar::U32, + }, + }, + Span::UNDEFINED, + ); + let ty_mat3x2f = self.types.insert( + crate::Type { + name: None, + inner: crate::TypeInner::Matrix { + columns: crate::VectorSize::Tri, + rows: crate::VectorSize::Bi, + scalar: crate::Scalar::F32, + }, + }, + Span::UNDEFINED, + ); + let ty_mat3x3f = self.types.insert( + crate::Type { + name: None, + inner: crate::TypeInner::Matrix { + columns: crate::VectorSize::Tri, + rows: crate::VectorSize::Tri, + scalar: crate::Scalar::F32, + }, + }, + Span::UNDEFINED, + ); + let ty_mat4x4f = self.types.insert( + crate::Type { + name: None, + inner: crate::TypeInner::Matrix { + columns: crate::VectorSize::Quad, + rows: crate::VectorSize::Quad, + scalar: crate::Scalar::F32, + }, + }, + Span::UNDEFINED, + ); + + let transfer_fn_handle = self.types.insert( + crate::Type { + name: Some("NagaExternalTextureTransferFn".to_string()), + inner: crate::TypeInner::Struct { + members: vec![ + crate::StructMember { + name: Some("a".to_string()), + ty: ty_f32, + binding: None, + offset: 0, + }, + crate::StructMember { + name: Some("b".to_string()), + ty: ty_f32, + binding: None, + offset: 4, + }, + crate::StructMember { + name: Some("g".to_string()), + ty: ty_f32, + binding: None, + offset: 8, + }, + crate::StructMember { + name: Some("k".to_string()), + ty: ty_f32, + binding: None, + offset: 12, + }, + ], + span: 16, + }, + }, + Span::UNDEFINED, + ); + self.special_types.external_texture_transfer_function = Some(transfer_fn_handle); + + let params_handle = self.types.insert( + crate::Type { + name: Some("NagaExternalTextureParams".to_string()), + inner: crate::TypeInner::Struct { + members: vec![ + crate::StructMember { + name: Some("yuv_conversion_matrix".to_string()), + ty: ty_mat4x4f, + binding: None, + offset: 0, + }, + crate::StructMember { + name: Some("gamut_conversion_matrix".to_string()), + ty: ty_mat3x3f, + binding: None, + offset: 64, + }, + crate::StructMember { + name: Some("src_tf".to_string()), + ty: transfer_fn_handle, + binding: None, + offset: 112, + }, + crate::StructMember { + name: Some("dst_tf".to_string()), + ty: transfer_fn_handle, + binding: None, + offset: 128, + }, + crate::StructMember { + name: Some("sample_transform".to_string()), + ty: ty_mat3x2f, + binding: None, + offset: 144, + }, + crate::StructMember { + name: Some("load_transform".to_string()), + ty: ty_mat3x2f, + binding: None, + offset: 168, + }, + crate::StructMember { + name: Some("size".to_string()), + ty: ty_vec2u, + binding: None, + offset: 192, + }, + crate::StructMember { + name: Some("num_planes".to_string()), + ty: ty_u32, + binding: None, + offset: 200, + }, + ], + span: 208, + }, + }, + Span::UNDEFINED, + ); + self.special_types.external_texture_params = Some(params_handle); + } + /// Populate this module's [`SpecialTypes::predeclared_types`] type and return the handle. /// /// [`SpecialTypes::predeclared_types`]: crate::SpecialTypes::predeclared_types diff --git a/naga/src/front/wgsl/error.rs b/naga/src/front/wgsl/error.rs index 1123caff6a3..17dab5cb0ea 100644 --- a/naga/src/front/wgsl/error.rs +++ b/naga/src/front/wgsl/error.rs @@ -2,6 +2,7 @@ use crate::common::wgsl::TryToWgsl; use crate::diagnostic_filter::ConflictingDiagnosticRuleError; +use crate::error::replace_control_chars; use crate::proc::{Alignment, ConstantEvaluatorError, ResolveError}; use crate::{Scalar, SourceLocation, Span}; @@ -79,7 +80,7 @@ impl ParseError { P: AsRef, { let path = path.as_ref().display().to_string(); - let files = SimpleFile::new(path, source); + let files = SimpleFile::new(path, replace_control_chars(source)); let config = term::Config::default(); cfg_if::cfg_if! { @@ -105,7 +106,7 @@ impl ParseError { P: AsRef, { let path = path.as_ref().display().to_string(); - let files = SimpleFile::new(path, source); + let files = SimpleFile::new(path, replace_control_chars(source)); let config = term::Config::default(); let mut writer = crate::error::DiagnosticBuffer::new(); @@ -126,11 +127,7 @@ impl core::fmt::Display for ParseError { } } -impl core::error::Error for ParseError { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - None - } -} +impl core::error::Error for ParseError {} #[derive(Copy, Clone, Debug, PartialEq)] pub enum ExpectedToken<'a> { @@ -409,6 +406,12 @@ pub(crate) enum Error<'a> { accept_span: Span, accept_type: String, }, + StructMemberTooLarge { + member_name_span: Span, + }, + TypeTooLarge { + span: Span, + }, } impl From for Error<'_> { @@ -690,7 +693,7 @@ impl<'a> Error<'a> { notes: vec![], }, Error::UnknownEnableExtension(span, word) => ParseError { - message: format!("unknown enable-extension `{}`", word), + message: format!("unknown enable-extension `{word}`"), labels: vec![(span, "".into())], notes: vec![ "See available extensions at ." @@ -1078,8 +1081,7 @@ impl<'a> Error<'a> { } = **error; ParseError { message: format!( - "automatic conversions cannot convert `{}` to `{}`", - source_type, dest_type + "automatic conversions cannot convert `{source_type}` to `{dest_type}`" ), labels: vec![ ( @@ -1103,15 +1105,13 @@ impl<'a> Error<'a> { } = **error; ParseError { message: format!( - "automatic conversions cannot convert elements of `{}` to `{}`", - source_type, dest_scalar + "automatic conversions cannot convert elements of `{source_type}` to `{dest_scalar}`" ), labels: vec![ ( dest_span, format!( - "a value with elements of type {} is required here", - dest_scalar + "a value with elements of type {dest_scalar} is required here" ) .into(), ), @@ -1370,6 +1370,22 @@ impl<'a> Error<'a> { ], notes: vec![], }, + Error::StructMemberTooLarge { member_name_span } => ParseError { + message: "struct member is too large".into(), + labels: vec![(member_name_span, "this member exceeds the maximum size".into())], + notes: vec![format!( + "the maximum size is {} bytes", + crate::valid::MAX_TYPE_SIZE + )], + }, + Error::TypeTooLarge { span } => ParseError { + message: "type is too large".into(), + labels: vec![(span, "this type exceeds the maximum size".into())], + notes: vec![format!( + "the maximum size is {} bytes", + crate::valid::MAX_TYPE_SIZE + )], + }, } } } diff --git a/naga/src/front/wgsl/lower/mod.rs b/naga/src/front/wgsl/lower/mod.rs index 1f994d753f0..e90d7eab0a8 100644 --- a/naga/src/front/wgsl/lower/mod.rs +++ b/naga/src/front/wgsl/lower/mod.rs @@ -7,7 +7,6 @@ use alloc::{ }; use core::num::NonZeroU32; -use crate::common::ForDebugWithTypes; use crate::front::wgsl::error::{Error, ExpectedToken, InvalidAssignmentType}; use crate::front::wgsl::index::Index; use crate::front::wgsl::parse::number::Number; @@ -18,6 +17,7 @@ use crate::{ common::wgsl::{TryToWgsl, TypeContext}, compact::KeepUnused, }; +use crate::{common::ForDebugWithTypes, proc::LayoutErrorInner}; use crate::{ir, proc}; use crate::{Arena, FastHashMap, FastIndexMap, Handle, Span}; @@ -466,7 +466,7 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> { } } - fn as_const_evaluator(&mut self) -> proc::ConstantEvaluator { + fn as_const_evaluator(&mut self) -> proc::ConstantEvaluator<'_> { match self.expr_type { ExpressionContextType::Runtime(ref mut rctx) => { proc::ConstantEvaluator::for_wgsl_function( @@ -513,7 +513,7 @@ impl<'source, 'temp, 'out> ExpressionContext<'source, 'temp, 'out> { fn as_diagnostic_display( &self, value: T, - ) -> crate::common::DiagnosticDisplay<(T, proc::GlobalCtx)> { + ) -> crate::common::DiagnosticDisplay<(T, proc::GlobalCtx<'_>)> { let ctx = self.module.to_ctx(); crate::common::DiagnosticDisplay((value, ctx)) } @@ -982,7 +982,7 @@ impl Components { } } - fn single_component(name: &str, name_span: Span) -> Result { + fn single_component(name: &str, name_span: Span) -> Result<'_, u32> { let ch = name.chars().next().ok_or(Error::BadAccessor(name_span))?; match Self::letter_component(ch) { Some(sc) => Ok(sc as u32), @@ -993,7 +993,7 @@ impl Components { /// Construct a `Components` value from a 'member' name, like `"wzy"` or `"x"`. /// /// Use `name_span` for reporting errors in parsing the component string. - fn new(name: &str, name_span: Span) -> Result { + fn new(name: &str, name_span: Span) -> Result<'_, Self> { let size = match name.len() { 1 => return Ok(Components::Single(Self::single_component(name, name_span)?)), 2 => ir::VectorSize::Bi, @@ -3400,12 +3400,12 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner { ir::TypeInner::Atomic(scalar) => Ok((pointer, scalar)), ref other => { - log::error!("Pointer type to {:?} passed to atomic op", other); + log::error!("Pointer type to {other:?} passed to atomic op"); Err(Box::new(Error::InvalidAtomicPointer(span))) } }, ref other => { - log::error!("Type {:?} passed to atomic op", other); + log::error!("Type {other:?} passed to atomic op"); Err(Box::new(Error::InvalidAtomicPointer(span))) } } @@ -3587,9 +3587,12 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { self.expression_with_leaf_scalar(args.next()?, ir::Scalar::F32, ctx)? } - // Sampling `Storage` textures isn't allowed at all. Let the - // validator report the error. - ir::ImageClass::Storage { .. } => self.expression(args.next()?, ctx)?, + // Sampling `External` textures with a specified level isn't + // allowed, and sampling `Storage` textures isn't allowed at + // all. Let the validator report the error. + ir::ImageClass::Storage { .. } | ir::ImageClass::External => { + self.expression(args.next()?, ctx)? + } }; level = ir::SampleLevel::Exact(exact); depth_ref = None; @@ -3709,7 +3712,27 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { for member in s.members.iter() { let ty = self.resolve_ast_type(member.ty, &mut ctx.as_const())?; - ctx.layouter.update(ctx.module.to_ctx()).unwrap(); + ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| { + let LayoutErrorInner::TooLarge = err.inner else { + unreachable!("unexpected layout error: {err:?}"); + }; + // Since anonymous types of struct members don't get a span, + // associate the error with the member. The layouter could have + // failed on any type that was pending layout, but if it wasn't + // the current struct member, it wasn't a struct member at all, + // because we resolve struct members one-by-one. + if ty == err.ty { + Box::new(Error::StructMemberTooLarge { + member_name_span: member.name.span, + }) + } else { + // Lots of type definitions don't get spans, so this error + // message may not be very useful. + Box::new(Error::TypeTooLarge { + span: ctx.module.types.get_span(err.ty), + }) + } + })?; let member_min_size = ctx.layouter[ty].size; let member_min_alignment = ctx.layouter[ty].alignment; @@ -3761,6 +3784,9 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { }); offset += member_size; + if offset > crate::valid::MAX_TYPE_SIZE { + return Err(Box::new(Error::TypeTooLarge { span })); + } } let size = struct_alignment.round_up(offset); @@ -3938,7 +3964,17 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { let base = self.resolve_ast_type(base, &mut ctx.as_const())?; let size = self.array_size(size, ctx)?; - ctx.layouter.update(ctx.module.to_ctx()).unwrap(); + // Determine the size of the base type, if needed. + ctx.layouter.update(ctx.module.to_ctx()).map_err(|err| { + let LayoutErrorInner::TooLarge = err.inner else { + unreachable!("unexpected layout error: {err:?}"); + }; + // Lots of type definitions don't get spans, so this error + // message may not be very useful. + Box::new(Error::TypeTooLarge { + span: ctx.module.types.get_span(err.ty), + }) + })?; let stride = ctx.layouter[base].to_stride(); ir::TypeInner::Array { base, size, stride } @@ -3947,11 +3983,30 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { dim, arrayed, class, - } => ir::TypeInner::Image { - dim, - arrayed, - class, - }, + } => { + if class == crate::ImageClass::External { + // Other than the WGSL backend, every backend that supports + // external textures does so by lowering them to a set of + // ordinary textures and some parameters saying how to + // sample from them. We don't know which backend will + // consume the `Module` we're building, but in case it's not + // WGSL, populate `SpecialTypes::external_texture_params` + // and `SpecialTypes::external_texture_transfer_function` + // with the types the backend will use for the parameter + // buffer. + // + // Neither of these are the type we are lowering here: + // that's an ordinary `TypeInner::Image`. But the fact we + // are lowering a `texture_external` implies the backends + // may need these additional types too. + ctx.module.generate_external_texture_types(); + } + ir::TypeInner::Image { + dim, + arrayed, + class, + } + } ast::Type::Sampler { comparison } => ir::TypeInner::Sampler { comparison }, ast::Type::AccelerationStructure { vertex_return } => { ir::TypeInner::AccelerationStructure { vertex_return } @@ -4034,12 +4089,12 @@ impl<'source, 'temp> Lowerer<'source, 'temp> { ir::TypeInner::Pointer { base, .. } => match ctx.module.types[base].inner { ir::TypeInner::RayQuery { .. } => Ok(pointer), ref other => { - log::error!("Pointer type to {:?} passed to ray query op", other); + log::error!("Pointer type to {other:?} passed to ray query op"); Err(Box::new(Error::InvalidRayQueryPointer(span))) } }, ref other => { - log::error!("Type {:?} passed to ray query op", other); + log::error!("Type {other:?} passed to ray query op"); Err(Box::new(Error::InvalidRayQueryPointer(span))) } } diff --git a/naga/src/front/wgsl/mod.rs b/naga/src/front/wgsl/mod.rs index 1080392cc61..ba41dc80128 100644 --- a/naga/src/front/wgsl/mod.rs +++ b/naga/src/front/wgsl/mod.rs @@ -11,6 +11,10 @@ mod parse; #[cfg(test)] mod tests; +pub use parse::directive::enable_extension::{ + EnableExtension, ImplementedEnableExtension, UnimplementedEnableExtension, +}; + pub use crate::front::wgsl::error::ParseError; pub use crate::front::wgsl::parse::directive::language_extension::{ ImplementedLanguageExtension, LanguageExtension, UnimplementedLanguageExtension, @@ -48,6 +52,9 @@ impl Frontend { options, } } + pub fn set_options(&mut self, options: Options) { + self.options = options; + } pub fn parse(&mut self, source: &str) -> core::result::Result { self.inner(source).map_err(|x| x.as_parse_error(source)) diff --git a/naga/src/front/wgsl/parse/conv.rs b/naga/src/front/wgsl/parse/conv.rs index cbc485fb24a..30d0eb2d598 100644 --- a/naga/src/front/wgsl/parse/conv.rs +++ b/naga/src/front/wgsl/parse/conv.rs @@ -115,7 +115,7 @@ pub fn map_storage_format(word: &str, span: Span) -> Result<'_, crate::StorageFo "rgba8sint" => Sf::Rgba8Sint, "rgb10a2uint" => Sf::Rgb10a2Uint, "rgb10a2unorm" => Sf::Rgb10a2Unorm, - "rg11b10float" => Sf::Rg11b10Ufloat, + "rg11b10ufloat" => Sf::Rg11b10Ufloat, "r64uint" => Sf::R64Uint, "rg32uint" => Sf::Rg32Uint, "rg32sint" => Sf::Rg32Sint, diff --git a/naga/src/front/wgsl/parse/directive/enable_extension.rs b/naga/src/front/wgsl/parse/directive/enable_extension.rs index 7064e40fb43..38d6d6719ca 100644 --- a/naga/src/front/wgsl/parse/directive/enable_extension.rs +++ b/naga/src/front/wgsl/parse/directive/enable_extension.rs @@ -71,9 +71,10 @@ impl EnableExtension { const CLIP_DISTANCES: &'static str = "clip_distances"; const DUAL_SOURCE_BLENDING: &'static str = "dual_source_blending"; const SUBGROUPS: &'static str = "subgroups"; + const PRIMITIVE_INDEX: &'static str = "primitive_index"; /// Convert from a sentinel word in WGSL into its associated [`EnableExtension`], if possible. - pub(crate) fn from_ident(word: &str, span: Span) -> Result { + pub(crate) fn from_ident(word: &str, span: Span) -> Result<'_, Self> { Ok(match word { Self::F16 => Self::Implemented(ImplementedEnableExtension::F16), Self::CLIP_DISTANCES => Self::Implemented(ImplementedEnableExtension::ClipDistances), @@ -81,6 +82,9 @@ impl EnableExtension { Self::Implemented(ImplementedEnableExtension::DualSourceBlending) } Self::SUBGROUPS => Self::Unimplemented(UnimplementedEnableExtension::Subgroups), + Self::PRIMITIVE_INDEX => { + Self::Unimplemented(UnimplementedEnableExtension::PrimitiveIndex) + } _ => return Err(Box::new(Error::UnknownEnableExtension(span, word))), }) } @@ -95,6 +99,7 @@ impl EnableExtension { }, Self::Unimplemented(kind) => match kind { UnimplementedEnableExtension::Subgroups => Self::SUBGROUPS, + UnimplementedEnableExtension::PrimitiveIndex => Self::PRIMITIVE_INDEX, }, } } @@ -132,12 +137,19 @@ pub enum UnimplementedEnableExtension { /// /// [`enable subgroups;`]: https://www.w3.org/TR/WGSL/#extension-subgroups Subgroups, + /// Enables the `@builtin(primitive_index)` attribute in WGSL. + /// + /// In the WGSL standard, this corresponds to [`enable primitive-index;`]. + /// + /// [`enable primitive-index;`]: https://www.w3.org/TR/WGSL/#extension-primitive_index + PrimitiveIndex, } impl UnimplementedEnableExtension { pub(crate) const fn tracking_issue_num(self) -> u16 { match self { Self::Subgroups => 5555, + Self::PrimitiveIndex => 8236, } } } diff --git a/naga/src/front/wgsl/parse/mod.rs b/naga/src/front/wgsl/parse/mod.rs index 892930b4301..c01ba4de30f 100644 --- a/naga/src/front/wgsl/parse/mod.rs +++ b/naga/src/front/wgsl/parse/mod.rs @@ -235,6 +235,7 @@ impl<'a> BindingParser<'a> { lexer.expect(Token::Paren('('))?; self.blend_src .set(parser.general_expression(lexer, ctx)?, name_span)?; + lexer.skip(Token::Separator(',')); lexer.expect(Token::Paren(')'))?; } _ => return Err(Box::new(Error::UnknownAttribute(name_span))), @@ -676,6 +677,7 @@ impl Parser { | "texture_depth_cube" | "texture_depth_cube_array" | "texture_depth_multisampled_2d" + | "texture_external" | "texture_storage_1d" | "texture_storage_1d_array" | "texture_storage_2d" @@ -1867,6 +1869,11 @@ impl Parser { arrayed: false, class: crate::ImageClass::Depth { multi: true }, }, + "texture_external" => ast::Type::Image { + dim: crate::ImageDimension::D2, + arrayed: false, + class: crate::ImageClass::External, + }, "texture_storage_1d" => { let (format, access) = lexer.next_format_generic()?; ast::Type::Image { @@ -2114,7 +2121,7 @@ impl Parser { let _ = lexer.next(); this.pop_rule_span(lexer); } - (Token::Paren('{') | Token::Attribute, _) => { + (token, _) if is_start_of_compound_statement(token) => { let (inner, span) = this.block(lexer, ctx, brace_nesting_level)?; block.stmts.push(ast::Statement { kind: ast::StatementKind::Block(inner), @@ -2280,11 +2287,14 @@ impl Parser { let value = loop { let value = this.switch_value(lexer, ctx)?; if lexer.skip(Token::Separator(',')) { - if lexer.skip(Token::Separator(':')) { + // list of values ends with ':' or a compound statement + let next_token = lexer.peek().0; + if next_token == Token::Separator(':') + || is_start_of_compound_statement(next_token) + { break value; } } else { - lexer.skip(Token::Separator(':')); break value; } cases.push(ast::SwitchCase { @@ -2294,6 +2304,8 @@ impl Parser { }); }; + lexer.skip(Token::Separator(':')); + let body = this.block(lexer, ctx, brace_nesting_level)?.0; cases.push(ast::SwitchCase { @@ -3237,3 +3249,7 @@ impl Parser { }) } } + +const fn is_start_of_compound_statement<'a>(token: Token<'a>) -> bool { + matches!(token, Token::Attribute | Token::Paren('{')) +} diff --git a/naga/src/front/wgsl/tests.rs b/naga/src/front/wgsl/tests.rs index bdcd35d8a86..149f5d329fd 100644 --- a/naga/src/front/wgsl/tests.rs +++ b/naga/src/front/wgsl/tests.rs @@ -627,7 +627,7 @@ fn parse_texture_load_store_expecting_four_args() { for (func, texture) in [ ( "textureStore", - "texture_storage_2d_array", + "texture_storage_2d_array", ), ("textureLoad", "texture_2d_array"), ] { diff --git a/naga/src/ir/mod.rs b/naga/src/ir/mod.rs index 943dbc2458b..257445952b8 100644 --- a/naga/src/ir/mod.rs +++ b/naga/src/ir/mod.rs @@ -347,7 +347,21 @@ pub enum AddressSpace { Storage { access: StorageAccess }, /// Opaque handles, such as samplers and images. Handle, + /// Push constants. + /// + /// A [`Module`] may contain at most one [`GlobalVariable`] in + /// this address space. Its contents are provided not by a buffer + /// but by `SetPushConstant` pass commands, allowing the CPU to + /// establish different values for each draw/dispatch. + /// + /// `PushConstant` variables may not contain `f16` values, even if + /// the [`SHADER_FLOAT16`] capability is enabled. + /// + /// Backends generally place tight limits on the size of + /// `PushConstant` variables. + /// + /// [`SHADER_FLOAT16`]: crate::valid::Capabilities::SHADER_FLOAT16 PushConstant, } @@ -411,6 +425,18 @@ impl VectorSize { pub const MAX: usize = Self::Quad as usize; } +impl From for u8 { + fn from(size: VectorSize) -> u8 { + size as u8 + } +} + +impl From for u32 { + fn from(size: VectorSize) -> u32 { + size as u32 + } +} + /// Primitive type for a scalar. #[repr(u8)] #[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)] @@ -640,6 +666,8 @@ pub enum ImageClass { /// Multi-sampled depth image. multi: bool, }, + /// External texture. + External, /// Storage image. Storage { format: StorageFormat, @@ -2332,6 +2360,51 @@ pub struct SpecialTypes { /// Call [`Module::generate_vertex_return_type`] pub ray_vertex_return: Option>, + /// Struct containing parameters required by some backends to emit code for + /// [`ImageClass::External`] textures. + /// + /// See `wgpu_core::device::resource::ExternalTextureParams` for the + /// documentation of each field. + /// + /// In WGSL, this type would be: + /// + /// ```ignore + /// struct NagaExternalTextureParams { // align size offset + /// yuv_conversion_matrix: mat4x4, // 16 64 0 + /// gamut_conversion_matrix: mat3x3, // 16 48 64 + /// src_tf: NagaExternalTextureTransferFn, // 4 16 112 + /// dst_tf: NagaExternalTextureTransferFn, // 4 16 128 + /// sample_transform: mat3x2, // 8 24 144 + /// load_transform: mat3x2, // 8 24 168 + /// size: vec2, // 8 8 192 + /// num_planes: u32, // 4 4 200 + /// } // whole struct: 16 208 + /// ``` + /// + /// Call [`Module::generate_external_texture_types`] to populate this if + /// needed. + pub external_texture_params: Option>, + + /// Struct describing a gamma encoding transfer function. Member of + /// `NagaExternalTextureParams`, describing how the backend should perform + /// color space conversion when sampling from [`ImageClass::External`] + /// textures. + /// + /// In WGSL, this type would be: + /// + /// ```ignore + /// struct NagaExternalTextureTransferFn { // align size offset + /// a: f32, // 4 4 0 + /// b: f32, // 4 4 4 + /// g: f32, // 4 4 8 + /// k: f32, // 4 4 12 + /// } // whole struct: 4 16 + /// ``` + /// + /// Call [`Module::generate_external_texture_types`] to populate this if + /// needed. + pub external_texture_transfer_function: Option>, + /// Types for predeclared wgsl types instantiated on demand. /// /// Call [`Module::generate_predeclared_type`] to populate this if diff --git a/naga/src/keywords/wgsl.rs b/naga/src/keywords/wgsl.rs index df464a10ff4..8ec0dd59c5f 100644 --- a/naga/src/keywords/wgsl.rs +++ b/naga/src/keywords/wgsl.rs @@ -4,10 +4,9 @@ Keywords for [WGSL][wgsl] (WebGPU Shading Language). [wgsl]: https://gpuweb.github.io/gpuweb/wgsl.html */ +use crate::proc::KeywordSet; use crate::racy_lock::RacyLock; -use hashbrown::HashSet; - // https://gpuweb.github.io/gpuweb/wgsl/#keyword-summary // last sync: https://github.com/gpuweb/gpuweb/blob/39f2321f547c8f0b7f473cf1d47fba30b1691303/wgsl/index.bs pub const RESERVED: &[&str] = &[ @@ -37,6 +36,7 @@ pub const RESERVED: &[&str] = &[ "texture_3d", "texture_cube", "texture_cube_array", + "texture_external", "texture_multisampled_2d", "texture_storage_1d", "texture_storage_2d", @@ -238,11 +238,4 @@ pub const RESERVED: &[&str] = &[ /// significant time during [`Namer::reset`](crate::proc::Namer::reset). /// /// See for benchmarks. -pub static RESERVED_SET: RacyLock> = RacyLock::new(|| { - let mut set = HashSet::default(); - set.reserve(RESERVED.len()); - for &word in RESERVED { - set.insert(word); - } - set -}); +pub static RESERVED_SET: RacyLock = RacyLock::new(|| KeywordSet::from_iter(RESERVED)); diff --git a/naga/src/lib.rs b/naga/src/lib.rs index bdd5345c312..af990fb1dd1 100644 --- a/naga/src/lib.rs +++ b/naga/src/lib.rs @@ -75,7 +75,8 @@ void main() { clippy::derive_partial_eq_without_eq, clippy::needless_borrowed_reference, clippy::single_match, - clippy::enum_variant_names + clippy::enum_variant_names, + clippy::result_large_err )] #![warn( trivial_casts, @@ -115,7 +116,6 @@ pub mod front; pub mod ir; pub mod keywords; mod non_max_u32; -mod path_like; pub mod proc; mod racy_lock; mod span; diff --git a/naga/src/path_like.rs b/naga/src/path_like.rs deleted file mode 100644 index 36ca39c493b..00000000000 --- a/naga/src/path_like.rs +++ /dev/null @@ -1,238 +0,0 @@ -//! [`PathLike`] and its supporting items, such as [`PathLikeRef`] and [`PathLikeOwned`]. -//! This trait and these types provide a common denominator API for `Path`-like -//! types and operations in `std` and `no_std` contexts. -//! -//! # Usage -//! -//! - Store a [`PathLikeRef<'a>`] instead of a `&'a Path` in structs and enums. -//! - Store a [`PathLikeOwned`] instead of a `PathBuf` in structs and enums. -//! - Accept `impl PathLike` instead of `impl AsRef` for methods which directly -//! work with `Path`-like values. -//! - Accept `Into>` and/or `Into` in methods which -//! will store a `Path`-like value. - -use alloc::{borrow::Cow, string::String}; -use core::fmt; - -mod sealed { - /// Seal for [`PathLike`](super::PathLike). - pub trait Sealed {} -} - -/// A trait that abstracts over types accepted for conversion to the most -/// featureful path representation possible; that is: -/// -/// - When `no_std` is active, this is implemented for: -/// - [`str`], -/// - [`String`], -/// - [`Cow<'_, str>`], and -/// - [`PathLikeRef`] -/// - Otherwise, types that implement `AsRef` (to extract a `&Path`). -/// -/// This type is used as the type bounds for various diagnostic rendering methods, i.e., -/// [`WithSpan::emit_to_string_with_path`](crate::span::WithSpan::emit_to_string_with_path). -pub trait PathLike: sealed::Sealed { - fn to_string_lossy(&self) -> Cow<'_, str>; -} - -/// Abstraction over `Path` which falls back to [`str`] for `no_std` compatibility. -/// -/// This type should be used for _storing_ a reference to a [`PathLike`]. -/// Functions which accept a `Path` should prefer to use `impl PathLike` -/// or `impl Into>`. -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PathLikeRef<'a>(&'a path_like_impls::PathInner); - -impl fmt::Debug for PathLikeRef<'_> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(self.0, f) - } -} - -impl<'a> From<&'a str> for PathLikeRef<'a> { - fn from(value: &'a str) -> Self { - cfg_if::cfg_if! { - if #[cfg(std)] { - Self(std::path::Path::new(value)) - } else { - Self(value) - } - } - } -} - -/// Abstraction over `PathBuf` which falls back to [`String`] for `no_std` compatibility. -/// -/// This type should be used for _storing_ an owned [`PathLike`]. -/// Functions which accept a `PathBuf` should prefer to use `impl PathLike` -/// or `impl Into`. -#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct PathLikeOwned(::Owned); - -impl fmt::Debug for PathLikeOwned { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f) - } -} - -impl From for PathLikeOwned { - fn from(value: String) -> Self { - cfg_if::cfg_if! { - if #[cfg(std)] { - Self(value.into()) - } else { - Self(value) - } - } - } -} - -#[cfg(std)] -mod path_like_impls { - //! Implementations of [`PathLike`] within an `std` context. - //! - //! Since `std` is available, we blanket implement [`PathLike`] for all types - //! implementing [`AsRef`]. - - use alloc::borrow::Cow; - use std::path::Path; - - use super::{sealed, PathLike}; - - pub(super) type PathInner = Path; - - impl + ?Sized> PathLike for T { - fn to_string_lossy(&self) -> Cow<'_, str> { - self.as_ref().to_string_lossy() - } - } - - impl + ?Sized> sealed::Sealed for T {} -} - -#[cfg(no_std)] -mod path_like_impls { - //! Implementations of [`PathLike`] within a `no_std` context. - //! - //! Without `std`, we cannot blanket implement on [`AsRef`]. - //! Instead, we manually implement for a subset of types which are known - //! to implement [`AsRef`] when `std` is available. - //! - //! Implementing [`PathLike`] for a type which does _not_ implement [`AsRef`] - //! with `std` enabled breaks the additive requirement of Cargo features. - - use alloc::{borrow::Cow, string::String}; - use core::borrow::Borrow; - - use super::{sealed, PathLike, PathLikeOwned, PathLikeRef}; - - pub(super) type PathInner = str; - - impl PathLike for String { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self.as_str()) - } - } - - impl sealed::Sealed for String {} - - impl PathLike for str { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self) - } - } - - impl sealed::Sealed for str {} - - impl PathLike for Cow<'_, str> { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self.borrow()) - } - } - - impl sealed::Sealed for Cow<'_, str> {} - - impl PathLike for &T { - fn to_string_lossy(&self) -> Cow<'_, str> { - (*self).to_string_lossy() - } - } - - impl sealed::Sealed for &T {} - - impl PathLike for PathLikeRef<'_> { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self.0) - } - } - - impl sealed::Sealed for PathLikeRef<'_> {} - - impl PathLike for PathLikeOwned { - fn to_string_lossy(&self) -> Cow<'_, str> { - Cow::Borrowed(self.0.borrow()) - } - } - - impl sealed::Sealed for PathLikeOwned {} -} - -#[cfg(std)] -mod path_like_owned_std_impls { - //! Traits which can only be implemented for [`PathLikeOwned`] with `std`. - - use std::path::{Path, PathBuf}; - - use super::PathLikeOwned; - - impl AsRef for PathLikeOwned { - fn as_ref(&self) -> &Path { - self.0.as_ref() - } - } - - impl From for PathLikeOwned { - fn from(value: PathBuf) -> Self { - Self(value) - } - } - - impl From for PathBuf { - fn from(value: PathLikeOwned) -> Self { - value.0 - } - } - - impl AsRef for PathLikeOwned { - fn as_ref(&self) -> &PathBuf { - &self.0 - } - } -} - -#[cfg(std)] -mod path_like_ref_std_impls { - //! Traits which can only be implemented for [`PathLikeRef`] with `std`. - - use std::path::Path; - - use super::PathLikeRef; - - impl AsRef for PathLikeRef<'_> { - fn as_ref(&self) -> &Path { - self.0 - } - } - - impl<'a> From<&'a Path> for PathLikeRef<'a> { - fn from(value: &'a Path) -> Self { - Self(value) - } - } - - impl<'a> From> for &'a Path { - fn from(value: PathLikeRef<'a>) -> Self { - value.0 - } - } -} diff --git a/naga/src/proc/constant_evaluator.rs b/naga/src/proc/constant_evaluator.rs index 4dba552f007..f5a5d25ca87 100644 --- a/naga/src/proc/constant_evaluator.rs +++ b/naga/src/proc/constant_evaluator.rs @@ -703,7 +703,7 @@ impl<'a> ConstantEvaluator<'a> { } } - pub fn to_ctx(&self) -> crate::proc::GlobalCtx { + pub fn to_ctx(&self) -> crate::proc::GlobalCtx<'_> { crate::proc::GlobalCtx { types: self.types, constants: self.constants, @@ -844,7 +844,7 @@ impl<'a> ConstantEvaluator<'a> { expr: &Expression, span: Span, ) -> Result, ConstantEvaluatorError> { - log::trace!("try_eval_and_append: {:?}", expr); + log::trace!("try_eval_and_append: {expr:?}"); match *expr { Expression::Constant(c) if self.is_global_arena() => { // "See through" the constant and use its initializer. @@ -1178,6 +1178,11 @@ impl<'a> ConstantEvaluator<'a> { crate::MathFunction::Atan => { component_wise_float!(self, span, [arg], |e| { Ok([e.atan()]) }) } + crate::MathFunction::Atan2 => { + component_wise_float!(self, span, [arg, arg1.unwrap()], |y, x| { + Ok([y.atan2(x)]) + }) + } crate::MathFunction::Asinh => { component_wise_float!(self, span, [arg], |e| { Ok([e.asinh()]) }) } @@ -1346,8 +1351,7 @@ impl<'a> ConstantEvaluator<'a> { crate::MathFunction::Cross => self.cross_product(arg, arg1.unwrap(), span), // unimplemented - crate::MathFunction::Atan2 - | crate::MathFunction::Modf + crate::MathFunction::Modf | crate::MathFunction::Frexp | crate::MathFunction::Ldexp | crate::MathFunction::Dot @@ -2816,11 +2820,11 @@ fn first_leading_bit_smoke() { trait TryFromAbstract: Sized { /// Convert an abstract literal `value` to `Self`. /// - /// Since Naga's `AbstractInt` and `AbstractFloat` exist to support + /// Since Naga's [`AbstractInt`] and [`AbstractFloat`] exist to support /// WGSL, we follow WGSL's conversion rules here: /// /// - WGSL §6.1.2. Conversion Rank says that automatic conversions - /// from `AbstractInt` to an integer type are either lossless or an + /// from [`AbstractInt`] to an integer type are either lossless or an /// error. /// /// - WGSL §15.7.6 Floating Point Conversion says that conversions @@ -2834,7 +2838,7 @@ trait TryFromAbstract: Sized { /// conversion from AbstractFloat to integer types. /// /// [`AbstractInt`]: crate::Literal::AbstractInt - /// [`Float`]: crate::Literal::Float + /// [`AbstractFloat`]: crate::Literal::AbstractFloat fn try_from_abstract(value: T) -> Result; } diff --git a/naga/src/proc/index.rs b/naga/src/proc/index.rs index cf6a127aced..87eaac7775c 100644 --- a/naga/src/proc/index.rs +++ b/naga/src/proc/index.rs @@ -99,8 +99,8 @@ pub struct BoundsCheckPolicies { /// /// [`GlobalVariable`]: crate::GlobalVariable /// [`space`]: crate::GlobalVariable::space - /// [`Restrict`]: crate::back::BoundsCheckPolicy::Restrict - /// [`ReadZeroSkipWrite`]: crate::back::BoundsCheckPolicy::ReadZeroSkipWrite + /// [`Restrict`]: crate::proc::BoundsCheckPolicy::Restrict + /// [`ReadZeroSkipWrite`]: crate::proc::BoundsCheckPolicy::ReadZeroSkipWrite /// [`Access`]: crate::Expression::Access /// [`AccessIndex`]: crate::Expression::AccessIndex /// [`Storage`]: crate::AddressSpace::Storage diff --git a/naga/src/proc/keyword_set.rs b/naga/src/proc/keyword_set.rs new file mode 100644 index 00000000000..c2ad5dd5516 --- /dev/null +++ b/naga/src/proc/keyword_set.rs @@ -0,0 +1,178 @@ +use core::{fmt, hash}; + +use crate::racy_lock::RacyLock; +use crate::FastHashSet; + +/// A case-sensitive set of strings, +/// for use with [`Namer`][crate::proc::Namer] to avoid collisions with keywords and other reserved +/// identifiers. +/// +/// This is currently implemented as a hash table. +/// Future versions of Naga may change the implementation based on speed and code size +/// considerations. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct KeywordSet(FastHashSet<&'static str>); + +impl KeywordSet { + /// Returns a new mutable empty set. + pub fn new() -> Self { + Self::default() + } + + /// Returns a reference to the empty set. + pub fn empty() -> &'static Self { + static EMPTY: RacyLock = RacyLock::new(Default::default); + &EMPTY + } + + /// Returns whether the set contains the given string. + #[inline] + pub fn contains(&self, identifier: &str) -> bool { + self.0.contains(identifier) + } +} + +impl Default for &'static KeywordSet { + fn default() -> Self { + KeywordSet::empty() + } +} + +impl FromIterator<&'static str> for KeywordSet { + fn from_iter>(iter: T) -> Self { + Self(iter.into_iter().collect()) + } +} + +/// Accepts double references so that `KeywordSet::from_iter(&["foo"])` works. +impl<'a> FromIterator<&'a &'static str> for KeywordSet { + fn from_iter>(iter: T) -> Self { + Self::from_iter(iter.into_iter().copied()) + } +} + +impl Extend<&'static str> for KeywordSet { + #[expect( + clippy::useless_conversion, + reason = "doing .into_iter() sooner reduces distinct monomorphizations" + )] + fn extend>(&mut self, iter: T) { + self.0.extend(iter.into_iter()) + } +} + +/// Accepts double references so that `.extend(&["foo"])` works. +impl<'a> Extend<&'a &'static str> for KeywordSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().copied()) + } +} + +/// A case-insensitive, ASCII-only set of strings, +/// for use with [`Namer`][crate::proc::Namer] to avoid collisions with keywords and other reserved +/// identifiers. +/// +/// This is currently implemented as a hash table. +/// Future versions of Naga may change the implementation based on speed and code size +/// considerations. +#[derive(Clone, Debug, Default, Eq, PartialEq)] +pub struct CaseInsensitiveKeywordSet(FastHashSet>); + +impl CaseInsensitiveKeywordSet { + /// Returns a new mutable empty set. + pub fn new() -> Self { + Self::default() + } + + /// Returns a reference to the empty set. + pub fn empty() -> &'static Self { + static EMPTY: RacyLock = RacyLock::new(Default::default); + &EMPTY + } + + /// Returns whether the set contains the given string, with comparison + /// by [`str::eq_ignore_ascii_case()`]. + #[inline] + pub fn contains(&self, identifier: &str) -> bool { + self.0.contains(&AsciiUniCase(identifier)) + } +} + +impl Default for &'static CaseInsensitiveKeywordSet { + fn default() -> Self { + CaseInsensitiveKeywordSet::empty() + } +} + +impl FromIterator<&'static str> for CaseInsensitiveKeywordSet { + fn from_iter>(iter: T) -> Self { + Self( + iter.into_iter() + .inspect(debug_assert_ascii) + .map(AsciiUniCase) + .collect(), + ) + } +} + +/// Accepts double references so that `CaseInsensitiveKeywordSet::from_iter(&["foo"])` works. +impl<'a> FromIterator<&'a &'static str> for CaseInsensitiveKeywordSet { + fn from_iter>(iter: T) -> Self { + Self::from_iter(iter.into_iter().copied()) + } +} + +impl Extend<&'static str> for CaseInsensitiveKeywordSet { + fn extend>(&mut self, iter: T) { + self.0.extend( + iter.into_iter() + .inspect(debug_assert_ascii) + .map(AsciiUniCase), + ) + } +} + +/// Accepts double references so that `.extend(&["foo"])` works. +impl<'a> Extend<&'a &'static str> for CaseInsensitiveKeywordSet { + fn extend>(&mut self, iter: T) { + self.extend(iter.into_iter().copied()) + } +} + +/// A string wrapper type with an ascii case insensitive Eq and Hash impl +#[derive(Clone, Copy)] +struct AsciiUniCase + ?Sized>(S); + +impl> fmt::Debug for AsciiUniCase { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.0.as_ref().fmt(f) + } +} + +impl> PartialEq for AsciiUniCase { + #[inline] + fn eq(&self, other: &Self) -> bool { + self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref()) + } +} + +impl> Eq for AsciiUniCase {} + +impl> hash::Hash for AsciiUniCase { + #[inline] + fn hash(&self, hasher: &mut H) { + for byte in self + .0 + .as_ref() + .as_bytes() + .iter() + .map(|b| b.to_ascii_lowercase()) + { + hasher.write_u8(byte); + } + } +} + +fn debug_assert_ascii(s: &&'static str) { + debug_assert!(s.is_ascii(), "{s:?} not ASCII") +} diff --git a/naga/src/proc/layouter.rs b/naga/src/proc/layouter.rs index 0001d92f28b..204a523c91b 100644 --- a/naga/src/proc/layouter.rs +++ b/naga/src/proc/layouter.rs @@ -1,6 +1,9 @@ use core::{fmt::Display, num::NonZeroU32, ops}; -use crate::arena::{Handle, HandleVec}; +use crate::{ + arena::{Handle, HandleVec}, + valid::MAX_TYPE_SIZE, +}; /// A newtype struct where its only valid values are powers of 2 #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] @@ -121,6 +124,12 @@ impl ops::Index> for Layouter { } } +/// Errors generated by the `Layouter`. +/// +/// All of these errors can be produced when validating an arbitrary module. +/// When processing WGSL source, only the `TooLarge` error should be +/// produced by the `Layouter`, as the front-end should not produce IR +/// that would result in the other errors. #[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)] pub enum LayoutErrorInner { #[error("Array element type {0:?} doesn't exist")] @@ -129,6 +138,8 @@ pub enum LayoutErrorInner { InvalidStructMemberType(u32, Handle), #[error("Type width must be a power of two")] NonPowerOfTwoWidth, + #[error("Size exceeds limit of {MAX_TYPE_SIZE} bytes")] + TooLarge, } #[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)] @@ -168,7 +179,10 @@ impl Layouter { use crate::TypeInner as Ti; for (ty_handle, ty) in gctx.types.iter().skip(self.layouts.len()) { - let size = ty.inner.size(gctx); + let size = ty + .inner + .try_size(gctx) + .ok_or_else(|| LayoutErrorInner::TooLarge.with(ty_handle))?; let layout = match ty.inner { Ti::Scalar(scalar) | Ti::Atomic(scalar) => { let alignment = Alignment::new(scalar.width as u32) diff --git a/naga/src/proc/mod.rs b/naga/src/proc/mod.rs index 0843e709b5d..26f873a9435 100644 --- a/naga/src/proc/mod.rs +++ b/naga/src/proc/mod.rs @@ -5,6 +5,7 @@ mod constant_evaluator; mod emitter; pub mod index; +mod keyword_set; mod layouter; mod namer; mod overloads; @@ -17,8 +18,9 @@ pub use constant_evaluator::{ }; pub use emitter::Emitter; pub use index::{BoundsCheckPolicies, BoundsCheckPolicy, IndexableLength, IndexableLengthError}; +pub use keyword_set::{CaseInsensitiveKeywordSet, KeywordSet}; pub use layouter::{Alignment, LayoutError, LayoutErrorInner, Layouter, TypeLayout}; -pub use namer::{EntryPointIndex, NameKey, Namer}; +pub use namer::{EntryPointIndex, ExternalTextureNameKey, NameKey, Namer}; pub use overloads::{Conclusion, MissingSpecialType, OverloadSet, Rule}; pub use terminator::ensure_block_returns; use thiserror::Error; @@ -383,6 +385,7 @@ impl super::ImageClass { match self { crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => multi, crate::ImageClass::Storage { .. } => false, + crate::ImageClass::External => false, } } @@ -390,6 +393,7 @@ impl super::ImageClass { match self { crate::ImageClass::Sampled { multi, .. } | crate::ImageClass::Depth { multi } => !multi, crate::ImageClass::Storage { .. } => false, + crate::ImageClass::External => false, } } diff --git a/naga/src/proc/namer.rs b/naga/src/proc/namer.rs index 74d28c4880e..127e346f3a1 100644 --- a/naga/src/proc/namer.rs +++ b/naga/src/proc/namer.rs @@ -1,20 +1,53 @@ use alloc::{ borrow::Cow, - boxed::Box, format, string::{String, ToString}, vec::Vec, }; -use core::hash::{Hash, Hasher}; -use hashbrown::HashSet; -use once_cell::race::OnceBox; - -use crate::{arena::Handle, FastHashMap, FastHashSet}; +use crate::{ + arena::Handle, + proc::{keyword_set::CaseInsensitiveKeywordSet, KeywordSet}, + FastHashMap, +}; pub type EntryPointIndex = u16; const SEPARATOR: char = '_'; +/// A component of a lowered external texture. +/// +/// Whereas the WGSL backend implements [`ImageClass::External`] +/// images directly, most other Naga backends lower them to a +/// collection of ordinary textures that represent individual planes +/// (as received from a video decoder, perhaps), together with a +/// struct of parameters saying how they should be cropped, sampled, +/// and color-converted. +/// +/// This lowering means that individual globals and function +/// parameters in Naga IR must be split out by the backends into +/// collections of globals and parameters of simpler types. +/// +/// A value of this enum serves as a name key for one specific +/// component in the lowered representation of an external texture. +/// That is, these keys are for variables/parameters that do not exist +/// in the Naga IR, only in its lowered form. +/// +/// [`ImageClass::External`]: crate::ir::ImageClass::External +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub enum ExternalTextureNameKey { + Plane(usize), + Params, +} + +impl ExternalTextureNameKey { + const ALL: &[(&str, ExternalTextureNameKey)] = &[ + ("_plane0", ExternalTextureNameKey::Plane(0)), + ("_plane1", ExternalTextureNameKey::Plane(1)), + ("_plane2", ExternalTextureNameKey::Plane(2)), + ("_params", ExternalTextureNameKey::Params), + ]; +} + #[derive(Debug, Eq, Hash, PartialEq)] pub enum NameKey { Constant(Handle), @@ -37,31 +70,30 @@ pub enum NameKey { /// Entry point version of `FunctionOobLocal`. EntryPointOobLocal(EntryPointIndex, Handle), + + /// A global variable holding a component of a lowered external texture. + /// + /// See [`ExternalTextureNameKey`] for details. + ExternalTextureGlobalVariable(Handle, ExternalTextureNameKey), + + /// A function argument holding a component of a lowered external + /// texture. + /// + /// See [`ExternalTextureNameKey`] for details. + ExternalTextureFunctionArgument(Handle, u32, ExternalTextureNameKey), } /// This processor assigns names to all the things in a module /// that may need identifiers in a textual backend. +#[derive(Default)] pub struct Namer { /// The last numeric suffix used for each base name. Zero means "no suffix". unique: FastHashMap, - keywords: &'static HashSet<&'static str>, - keywords_case_insensitive: FastHashSet>, + keywords: &'static KeywordSet, + keywords_case_insensitive: &'static CaseInsensitiveKeywordSet, reserved_prefixes: Vec<&'static str>, } -impl Default for Namer { - fn default() -> Self { - static DEFAULT_KEYWORDS: OnceBox> = OnceBox::new(); - - Self { - unique: Default::default(), - keywords: DEFAULT_KEYWORDS.get_or_init(|| Box::new(HashSet::default())), - keywords_case_insensitive: Default::default(), - reserved_prefixes: Default::default(), - } - } -} - impl Namer { /// Return a form of `string` suitable for use as the base of an identifier. /// @@ -86,20 +118,37 @@ impl Namer { { Cow::Borrowed(string) } else { - let mut filtered = string - .chars() - .filter(|&c| c.is_ascii_alphanumeric() || c == '_') - .fold(String::new(), |mut s, c| { - if s.ends_with('_') && c == '_' { - return s; - } + let mut filtered = string.chars().fold(String::new(), |mut s, c| { + let c = match c { + // Make several common characters in C++-ish types become snake case + // separators. + ':' | '<' | '>' | ',' => '_', + c => c, + }; + let had_underscore_at_end = s.ends_with('_'); + if had_underscore_at_end && c == '_' { + return s; + } + if c.is_ascii_alphanumeric() || c == '_' { s.push(c); - s - }); + } else { + use core::fmt::Write as _; + if !s.is_empty() && !had_underscore_at_end { + s.push('_'); + } + write!(s, "u{:04x}_", c as u32).unwrap(); + } + s + }); let stripped_len = filtered.trim_end_matches(SEPARATOR).len(); filtered.truncate(stripped_len); if filtered.is_empty() { filtered.push_str("unnamed"); + } else if filtered.starts_with(|c: char| c.is_ascii_digit()) { + unreachable!( + "internal error: invalid identifier starting with ASCII digit {:?}", + filtered.chars().nth(0) + ) } Cow::Owned(filtered) }; @@ -146,13 +195,11 @@ impl Namer { let mut suffixed = base.to_string(); if base.ends_with(char::is_numeric) || self.keywords.contains(base.as_ref()) - || self - .keywords_case_insensitive - .contains(&AsciiUniCase(base.as_ref())) + || self.keywords_case_insensitive.contains(base.as_ref()) { suffixed.push(SEPARATOR); } - debug_assert!(!self.keywords.contains::(&suffixed)); + debug_assert!(!self.keywords.contains(&suffixed)); // `self.unique` wants to own its keys. This allocates only if we haven't // already done so earlier. self.unique.insert(base.into_owned(), 0); @@ -183,8 +230,8 @@ impl Namer { pub fn reset( &mut self, module: &crate::Module, - reserved_keywords: &'static HashSet<&'static str>, - reserved_keywords_case_insensitive: &[&'static str], + reserved_keywords: &'static KeywordSet, + reserved_keywords_case_insensitive: &'static CaseInsensitiveKeywordSet, reserved_prefixes: &[&'static str], output: &mut FastHashMap, ) { @@ -193,16 +240,7 @@ impl Namer { self.unique.clear(); self.keywords = reserved_keywords; - - debug_assert!(reserved_keywords_case_insensitive - .iter() - .all(|s| s.is_ascii())); - self.keywords_case_insensitive.clear(); - self.keywords_case_insensitive.extend( - reserved_keywords_case_insensitive - .iter() - .map(|string| (AsciiUniCase(*string))), - ); + self.keywords_case_insensitive = reserved_keywords_case_insensitive; // Choose fallback names for anonymous entry point return types. let mut entrypoint_type_fallbacks = FastHashMap::default(); @@ -272,6 +310,27 @@ impl Namer { for (index, arg) in fun.arguments.iter().enumerate() { let name = self.call_or(&arg.name, "param"); output.insert(NameKey::FunctionArgument(fun_handle, index as u32), name); + + if matches!( + module.types[arg.ty].inner, + crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } + ) { + let base = arg.name.as_deref().unwrap_or("param"); + for &(suffix, ext_key) in ExternalTextureNameKey::ALL { + let name = self.call(&format!("{base}_{suffix}")); + output.insert( + NameKey::ExternalTextureFunctionArgument( + fun_handle, + index as u32, + ext_key, + ), + name, + ); + } + } } for (handle, var) in fun.local_variables.iter() { let name = self.call_or(&var.name, "local"); @@ -282,6 +341,23 @@ impl Namer { for (handle, var) in module.global_variables.iter() { let name = self.call_or(&var.name, "global"); output.insert(NameKey::GlobalVariable(handle), name); + + if matches!( + module.types[var.ty].inner, + crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } + ) { + let base = var.name.as_deref().unwrap_or("global"); + for &(suffix, ext_key) in ExternalTextureNameKey::ALL { + let name = self.call(&format!("{base}_{suffix}")); + output.insert( + NameKey::ExternalTextureGlobalVariable(handle, ext_key), + name, + ); + } + } } for (handle, constant) in module.constants.iter() { @@ -301,33 +377,6 @@ impl Namer { } } -/// A string wrapper type with an ascii case insensitive Eq and Hash impl -struct AsciiUniCase + ?Sized>(S); - -impl> PartialEq for AsciiUniCase { - #[inline] - fn eq(&self, other: &Self) -> bool { - self.0.as_ref().eq_ignore_ascii_case(other.0.as_ref()) - } -} - -impl> Eq for AsciiUniCase {} - -impl> Hash for AsciiUniCase { - #[inline] - fn hash(&self, hasher: &mut H) { - for byte in self - .0 - .as_ref() - .as_bytes() - .iter() - .map(|b| b.to_ascii_lowercase()) - { - hasher.write_u8(byte); - } - } -} - #[test] fn test() { let mut namer = Namer::default(); diff --git a/naga/src/proc/overloads/regular.rs b/naga/src/proc/overloads/regular.rs index 3975592faed..0b180aa0e79 100644 --- a/naga/src/proc/overloads/regular.rs +++ b/naga/src/proc/overloads/regular.rs @@ -195,7 +195,7 @@ fn make_rule( let inner = size.to_inner(scalar); let arg = TypeResolution::Value(inner.clone()); Rule { - arguments: core::iter::repeat(arg.clone()).take(arity).collect(), + arguments: core::iter::repeat_n(arg.clone(), arity).collect(), conclusion: conclusion_rule.conclude(size, scalar), } } diff --git a/naga/src/proc/type_methods.rs b/naga/src/proc/type_methods.rs index 289756a6795..c59d524f13e 100644 --- a/naga/src/proc/type_methods.rs +++ b/naga/src/proc/type_methods.rs @@ -4,7 +4,7 @@ //! [`Scalar`]: crate::Scalar //! [`ScalarKind`]: crate::ScalarKind -use crate::ir; +use crate::{ir, valid::MAX_TYPE_SIZE}; use super::TypeResolution; @@ -190,18 +190,19 @@ impl crate::TypeInner { } } - /// Get the size of this type. - pub fn size(&self, gctx: super::GlobalCtx) -> u32 { + /// Attempt to calculate the size of this type. Returns `None` if the size + /// exceeds the limit of [`crate::valid::MAX_TYPE_SIZE`]. + pub fn try_size(&self, gctx: super::GlobalCtx) -> Option { match *self { - Self::Scalar(scalar) | Self::Atomic(scalar) => scalar.width as u32, - Self::Vector { size, scalar } => size as u32 * scalar.width as u32, + Self::Scalar(scalar) | Self::Atomic(scalar) => Some(scalar.width as u32), + Self::Vector { size, scalar } => Some(size as u32 * scalar.width as u32), // matrices are treated as arrays of aligned columns Self::Matrix { columns, rows, scalar, - } => super::Alignment::from(rows) * scalar.width as u32 * columns as u32, - Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN, + } => Some(super::Alignment::from(rows) * scalar.width as u32 * columns as u32), + Self::Pointer { .. } | Self::ValuePointer { .. } => Some(POINTER_SPAN), Self::Array { base: _, size, @@ -215,17 +216,35 @@ impl crate::TypeInner { // A dynamically-sized array has to have at least one element Ok(crate::proc::IndexableLength::Dynamic) => 1, }; - count * stride + if count > MAX_TYPE_SIZE { + // It shouldn't be possible to have an array of a zero-sized type, but + // let's check just in case. + None + } else { + count + .checked_mul(stride) + .filter(|size| *size <= MAX_TYPE_SIZE) + } } - Self::Struct { span, .. } => span, + Self::Struct { span, .. } => Some(span), Self::Image { .. } | Self::Sampler { .. } | Self::AccelerationStructure { .. } | Self::RayQuery { .. } - | Self::BindingArray { .. } => 0, + | Self::BindingArray { .. } => Some(0), } } + /// Get the size of this type. + /// + /// Panics if the size exceeds the limit of [`crate::valid::MAX_TYPE_SIZE`]. + /// Validated modules should not contain such types. Code working with + /// modules prior to validation should use [`Self::try_size`] and handle the + /// error appropriately. + pub fn size(&self, gctx: super::GlobalCtx) -> u32 { + self.try_size(gctx).expect("type is too large") + } + /// Return the canonical form of `self`, or `None` if it's already in /// canonical form. /// diff --git a/naga/src/proc/typifier.rs b/naga/src/proc/typifier.rs index f7d1ea48390..79b4f95e106 100644 --- a/naga/src/proc/typifier.rs +++ b/naga/src/proc/typifier.rs @@ -296,7 +296,7 @@ impl<'a> ResolveContext<'a> { }, Ti::BindingArray { base, .. } => Ti::Pointer { base, space }, ref other => { - log::error!("Access sub-type {:?}", other); + log::error!("Access sub-type {other:?}"); return Err(ResolveError::InvalidSubAccess { ty: base, indexed: false, @@ -306,7 +306,7 @@ impl<'a> ResolveContext<'a> { } Ti::BindingArray { base, .. } => TypeResolution::Handle(base), ref other => { - log::error!("Access type {:?}", other); + log::error!("Access type {other:?}"); return Err(ResolveError::InvalidAccess { expr: base, indexed: false, @@ -392,7 +392,7 @@ impl<'a> ResolveContext<'a> { } Ti::BindingArray { base, .. } => Ti::Pointer { base, space }, ref other => { - log::error!("Access index sub-type {:?}", other); + log::error!("Access index sub-type {other:?}"); return Err(ResolveError::InvalidSubAccess { ty: ty_base, indexed: true, @@ -401,7 +401,7 @@ impl<'a> ResolveContext<'a> { }), Ti::BindingArray { base, .. } => TypeResolution::Handle(base), ref other => { - log::error!("Access index type {:?}", other); + log::error!("Access index type {other:?}"); return Err(ResolveError::InvalidAccess { expr: base, indexed: true, @@ -412,7 +412,7 @@ impl<'a> ResolveContext<'a> { crate::Expression::Splat { size, value } => match *past(value)?.inner_with(types) { Ti::Scalar(scalar) => TypeResolution::Value(Ti::Vector { size, scalar }), ref other => { - log::error!("Scalar type {:?}", other); + log::error!("Scalar type {other:?}"); return Err(ResolveError::InvalidScalar(value)); } }, @@ -425,7 +425,7 @@ impl<'a> ResolveContext<'a> { TypeResolution::Value(Ti::Vector { size, scalar }) } ref other => { - log::error!("Vector type {:?}", other); + log::error!("Vector type {other:?}"); return Err(ResolveError::InvalidVector(vector)); } }, @@ -476,7 +476,7 @@ impl<'a> ResolveContext<'a> { None => Ti::Scalar(scalar), }), ref other => { - log::error!("Pointer type {:?}", other); + log::error!("Pointer type {other:?}"); return Err(ResolveError::InvalidPointer(pointer)); } }, @@ -496,7 +496,7 @@ impl<'a> ResolveContext<'a> { size: crate::VectorSize::Quad, }), ref other => { - log::error!("Image type {:?}", other); + log::error!("Image type {other:?}"); return Err(ResolveError::InvalidImage(image)); } }, @@ -512,9 +512,13 @@ impl<'a> ResolveContext<'a> { scalar: format.into(), size: crate::VectorSize::Quad, }, + crate::ImageClass::External => Ti::Vector { + scalar: crate::Scalar::F32, + size: crate::VectorSize::Quad, + }, }), ref other => { - log::error!("Image type {:?}", other); + log::error!("Image type {other:?}"); return Err(ResolveError::InvalidImage(image)); } }, @@ -532,7 +536,7 @@ impl<'a> ResolveContext<'a> { }, }, ref other => { - log::error!("Image type {:?}", other); + log::error!("Image type {other:?}"); return Err(ResolveError::InvalidImage(image)); } }, diff --git a/naga/src/racy_lock.rs b/naga/src/racy_lock.rs index 116808055a6..fcb70b7926b 100644 --- a/naga/src/racy_lock.rs +++ b/naga/src/racy_lock.rs @@ -1,11 +1,3 @@ -#![cfg_attr( - not(any(glsl_out, hlsl_out, msl_out, feature = "wgsl-in", wgsl_out)), - expect( - dead_code, - reason = "RacyLock is only required for the above configurations" - ) -)] - use alloc::boxed::Box; use once_cell::race::OnceBox; @@ -25,17 +17,13 @@ impl RacyLock { init, } } - - /// Loads the internal value, initializing it if required. - pub fn get(&self) -> &T { - self.inner.get_or_init(|| Box::new((self.init)())) - } } impl core::ops::Deref for RacyLock { type Target = T; + /// Loads the internal value, initializing it if required. fn deref(&self) -> &Self::Target { - self.get() + self.inner.get_or_init(|| Box::new((self.init)())) } } diff --git a/naga/src/span.rs b/naga/src/span.rs index 942846e46b5..adfe7ad607a 100644 --- a/naga/src/span.rs +++ b/naga/src/span.rs @@ -6,7 +6,7 @@ use alloc::{ }; use core::{error::Error, fmt, ops::Range}; -use crate::{path_like::PathLike, Arena, Handle, UniqueArena}; +use crate::{error::replace_control_chars, Arena, Handle, UniqueArena}; /// A source code span, used for error reporting. #[derive(Clone, Copy, Debug, PartialEq, Default)] @@ -283,15 +283,13 @@ impl WithSpan { /// Emits a summary of the error to standard error stream. #[cfg(feature = "stderr")] - pub fn emit_to_stderr_with_path

(&self, source: &str, path: P) + pub fn emit_to_stderr_with_path(&self, source: &str, path: &str) where E: Error, - P: PathLike, { use codespan_reporting::{files, term}; - let path = path.to_string_lossy(); - let files = files::SimpleFile::new(path, source); + let files = files::SimpleFile::new(path, replace_control_chars(source)); let config = term::Config::default(); cfg_if::cfg_if! { @@ -315,15 +313,13 @@ impl WithSpan { } /// Emits a summary of the error to a string. - pub fn emit_to_string_with_path

(&self, source: &str, path: P) -> String + pub fn emit_to_string_with_path(&self, source: &str, path: &str) -> String where E: Error, - P: PathLike, { use codespan_reporting::{files, term}; - let path = path.to_string_lossy(); - let files = files::SimpleFile::new(path, source); + let files = files::SimpleFile::new(path, replace_control_chars(source)); let config = term::Config::default(); let mut writer = crate::error::DiagnosticBuffer::new(); diff --git a/naga/src/valid/compose.rs b/naga/src/valid/compose.rs index 20df99cbf39..802ddb63956 100644 --- a/naga/src/valid/compose.rs +++ b/naga/src/valid/compose.rs @@ -32,10 +32,7 @@ pub fn validate_compose( } if comp_scalar == scalar => comp_size as u32, ref other => { log::error!( - "Vector component[{}] type {:?}, building {:?}", - index, - other, - scalar + "Vector component[{index}] type {other:?}, building {scalar:?}" ); return Err(ComposeError::ComponentType { index: index as u32, @@ -65,7 +62,7 @@ pub fn validate_compose( } for (index, comp_res) in component_resolutions.enumerate() { if comp_res.inner_with(gctx.types) != &inner { - log::error!("Matrix component[{}] type {:?}", index, comp_res); + log::error!("Matrix component[{index}] type {comp_res:?}"); return Err(ComposeError::ComponentType { index: index as u32, }); @@ -85,7 +82,7 @@ pub fn validate_compose( } for (index, comp_res) in component_resolutions.enumerate() { if !gctx.compare_types(&TypeResolution::Handle(base), &comp_res) { - log::error!("Array component[{}] type {:?}", index, comp_res); + log::error!("Array component[{index}] type {comp_res:?}"); return Err(ComposeError::ComponentType { index: index as u32, }); @@ -102,7 +99,7 @@ pub fn validate_compose( for (index, (member, comp_res)) in members.iter().zip(component_resolutions).enumerate() { if !gctx.compare_types(&TypeResolution::Handle(member.ty), &comp_res) { - log::error!("Struct component[{}] type {:?}", index, comp_res); + log::error!("Struct component[{index}] type {comp_res:?}"); return Err(ComposeError::ComponentType { index: index as u32, }); @@ -110,7 +107,7 @@ pub fn validate_compose( } } ref other => { - log::error!("Composing of {:?}", other); + log::error!("Composing of {other:?}"); return Err(ComposeError::Type(self_ty_handle)); } } diff --git a/naga/src/valid/expression.rs b/naga/src/valid/expression.rs index 8b73486b1fe..68023b5bf01 100644 --- a/naga/src/valid/expression.rs +++ b/naga/src/valid/expression.rs @@ -266,7 +266,7 @@ impl super::Validator { | Ti::ValuePointer { size: Some(_), .. } | Ti::BindingArray { .. } => {} ref other => { - log::error!("Indexing of {:?}", other); + log::error!("Indexing of {other:?}"); return Err(ExpressionError::InvalidBaseType(base)); } }; @@ -277,7 +277,7 @@ impl super::Validator { .. }) => {} ref other => { - log::error!("Indexing by {:?}", other); + log::error!("Indexing by {other:?}"); return Err(ExpressionError::InvalidIndexType(index)); } } @@ -332,7 +332,7 @@ impl super::Validator { } Ti::Struct { ref members, .. } => members.len() as u32, ref other => { - log::error!("Indexing of {:?}", other); + log::error!("Indexing of {other:?}"); return Err(ExpressionError::InvalidBaseType(top)); } }; @@ -348,7 +348,7 @@ impl super::Validator { E::Splat { size: _, value } => match resolver[value] { Ti::Scalar { .. } => ShaderStages::all(), ref other => { - log::error!("Splat scalar type {:?}", other); + log::error!("Splat scalar type {other:?}"); return Err(ExpressionError::InvalidSplatType(value)); } }, @@ -360,7 +360,7 @@ impl super::Validator { let vec_size = match resolver[vector] { Ti::Vector { size: vec_size, .. } => vec_size, ref other => { - log::error!("Swizzle vector type {:?}", other); + log::error!("Swizzle vector type {other:?}"); return Err(ExpressionError::InvalidVectorType(vector)); } }; @@ -400,7 +400,7 @@ impl super::Validator { .contains(TypeFlags::SIZED | TypeFlags::DATA) => {} Ti::ValuePointer { .. } => {} ref other => { - log::error!("Loading {:?}", other); + log::error!("Loading {other:?}"); return Err(ExpressionError::InvalidPointerType(pointer)); } } @@ -460,6 +460,7 @@ impl super::Validator { kind: crate::ScalarKind::Uint | crate::ScalarKind::Sint, multi: false, } if gather.is_some() => false, + crate::ImageClass::External => false, crate::ImageClass::Depth { multi: false } => true, _ => return Err(ExpressionError::InvalidImageClass(class)), }; @@ -551,7 +552,7 @@ impl super::Validator { crate::ImageClass::Sampled { kind: crate::ScalarKind::Float, multi: false - } + } | crate::ImageClass::External ) { return Err(ExpressionError::InvalidSampleClampCoordinateToEdge( alloc::format!("image class `{class:?}`"), @@ -589,6 +590,11 @@ impl super::Validator { } } + // External textures can only be sampled using clamp_to_edge. + if matches!(class, crate::ImageClass::External) && !clamp_to_edge { + return Err(ExpressionError::InvalidImageClass(class)); + } + // check level properties match level { crate::SampleLevel::Auto => ShaderStages::FRAGMENT, @@ -766,7 +772,7 @@ impl super::Validator { | (Uo::LogicalNot, Some(Sk::Bool)) | (Uo::BitwiseNot, Some(Sk::Sint | Sk::Uint)) => {} other => { - log::error!("Op {:?} kind {:?}", op, other); + log::error!("Op {op:?} kind {other:?}"); return Err(ExpressionError::InvalidUnaryOperandType(op, expr)); } } @@ -875,7 +881,7 @@ impl super::Validator { Sk::Bool | Sk::AbstractInt | Sk::AbstractFloat => false, }, ref other => { - log::error!("Op {:?} left type {:?}", op, other); + log::error!("Op {op:?} left type {other:?}"); false } } @@ -887,7 +893,7 @@ impl super::Validator { .. } => left_inner == right_inner, ref other => { - log::error!("Op {:?} left type {:?}", op, other); + log::error!("Op {op:?} left type {other:?}"); false } }, @@ -897,7 +903,7 @@ impl super::Validator { Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false, }, ref other => { - log::error!("Op {:?} left type {:?}", op, other); + log::error!("Op {op:?} left type {other:?}"); false } }, @@ -907,7 +913,7 @@ impl super::Validator { Sk::Bool | Sk::Float | Sk::AbstractInt | Sk::AbstractFloat => false, }, ref other => { - log::error!("Op {:?} left type {:?}", op, other); + log::error!("Op {op:?} left type {other:?}"); false } }, @@ -916,7 +922,7 @@ impl super::Validator { Ti::Scalar(scalar) => (Ok(None), scalar), Ti::Vector { size, scalar } => (Ok(Some(size)), scalar), ref other => { - log::error!("Op {:?} base type {:?}", op, other); + log::error!("Op {op:?} base type {other:?}"); (Err(()), Sc::BOOL) } }; @@ -927,7 +933,7 @@ impl super::Validator { scalar: Sc { kind: Sk::Uint, .. }, } => Ok(Some(size)), ref other => { - log::error!("Op {:?} shift type {:?}", op, other); + log::error!("Op {op:?} shift type {other:?}"); Err(()) } }; @@ -1032,7 +1038,7 @@ impl super::Validator { .. } => {} ref other => { - log::error!("All/Any of type {:?}", other); + log::error!("All/Any of type {other:?}"); return Err(ExpressionError::InvalidBooleanVector(argument)); } }, @@ -1040,7 +1046,7 @@ impl super::Validator { Ti::Scalar(scalar) | Ti::Vector { scalar, .. } if scalar.kind == Sk::Float => {} ref other => { - log::error!("Float test of type {:?}", other); + log::error!("Float test of type {other:?}"); return Err(ExpressionError::InvalidFloatArgument(argument)); } }, @@ -1054,6 +1060,20 @@ impl super::Validator { arg2, arg3, } => { + if matches!( + fun, + crate::MathFunction::QuantizeToF16 + | crate::MathFunction::Pack2x16float + | crate::MathFunction::Unpack2x16float + ) && !self + .capabilities + .contains(crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32) + { + return Err(ExpressionError::MissingCapabilities( + crate::valid::Capabilities::SHADER_FLOAT16_IN_FLOAT32, + )); + } + let actuals: &[_] = match (arg1, arg2, arg3) { (None, None, None) => &[arg], (Some(arg1), None, None) => &[arg, arg1], @@ -1164,7 +1184,7 @@ impl super::Validator { } } ref other => { - log::error!("Array length of {:?}", other); + log::error!("Array length of {other:?}"); return Err(ExpressionError::InvalidArrayType(expr)); } }, @@ -1179,12 +1199,12 @@ impl super::Validator { } => match resolver.types[base].inner { Ti::RayQuery { .. } => ShaderStages::all(), ref other => { - log::error!("Intersection result of a pointer to {:?}", other); + log::error!("Intersection result of a pointer to {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query)); } }, ref other => { - log::error!("Intersection result of {:?}", other); + log::error!("Intersection result of {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query)); } }, @@ -1200,12 +1220,12 @@ impl super::Validator { vertex_return: true, } => ShaderStages::all(), ref other => { - log::error!("Intersection result of a pointer to {:?}", other); + log::error!("Intersection result of a pointer to {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query)); } }, ref other => { - log::error!("Intersection result of {:?}", other); + log::error!("Intersection result of {other:?}"); return Err(ExpressionError::InvalidRayQueryType(query)); } }, diff --git a/naga/src/valid/function.rs b/naga/src/valid/function.rs index fb88f3934f5..dc19e191764 100644 --- a/naga/src/valid/function.rs +++ b/naga/src/valid/function.rs @@ -509,7 +509,7 @@ impl super::Validator { | crate::AtomicFunction::Subtract | crate::AtomicFunction::Exchange { compare: None } ) { - log::error!("Float32 atomic operation {:?} is not supported", fun); + log::error!("Float32 atomic operation {fun:?} is not supported"); return Err(AtomicError::InvalidOperator(*fun) .with_span_handle(value, context.expressions) .into_other()); @@ -639,7 +639,7 @@ impl super::Validator { crate::TypeInner::Scalar(scalar) => (true, scalar), crate::TypeInner::Vector { scalar, .. } => (false, scalar), _ => { - log::error!("Subgroup operand type {:?}", argument_inner); + log::error!("Subgroup operand type {argument_inner:?}"); return Err(SubgroupError::InvalidOperand(argument) .with_span_handle(argument, context.expressions) .into_other()); @@ -654,7 +654,7 @@ impl super::Validator { (sk::Sint | sk::Uint, sg::And | sg::Or | sg::Xor) => {} (_, _) => { - log::error!("Subgroup operand type {:?}", argument_inner); + log::error!("Subgroup operand type {argument_inner:?}"); return Err(SubgroupError::InvalidOperand(argument) .with_span_handle(argument, context.expressions) .into_other()); @@ -714,8 +714,7 @@ impl super::Validator { crate::TypeInner::Scalar(crate::Scalar::U32) => {} _ => { log::error!( - "Subgroup gather index type {:?}, expected unsigned int", - index_ty + "Subgroup gather index type {index_ty:?}, expected unsigned int" ); return Err(SubgroupError::InvalidOperand(argument) .with_span_handle(index, context.expressions) @@ -740,7 +739,7 @@ impl super::Validator { crate::TypeInner::Scalar ( scalar, .. ) | crate::TypeInner::Vector { scalar, .. } if matches!(scalar.kind, crate::ScalarKind::Uint | crate::ScalarKind::Sint | crate::ScalarKind::Float) ) { - log::error!("Subgroup gather operand type {:?}", argument_inner); + log::error!("Subgroup gather operand type {argument_inner:?}"); return Err(SubgroupError::InvalidOperand(argument) .with_span_handle(argument, context.expressions) .into_other()); @@ -1567,8 +1566,7 @@ impl super::Validator { crate::TypeInner::Scalar(crate::Scalar::BOOL,) ) { log::error!( - "Subgroup ballot predicate type {:?} expected bool", - predicate_inner + "Subgroup ballot predicate type {predicate_inner:?} expected bool" ); return Err(SubgroupError::InvalidOperand(predicate) .with_span_handle(predicate, context.expressions) @@ -1645,7 +1643,7 @@ impl super::Validator { fun_info: &FunctionInfo, local_expr_kind: &crate::proc::ExpressionKindTracker, ) -> Result<(), LocalVariableError> { - log::debug!("var {:?}", var); + log::debug!("var {var:?}"); let type_info = self .types .get(var.ty.index()) diff --git a/naga/src/valid/interface.rs b/naga/src/valid/interface.rs index 14cfa42e8a4..7c8cc903139 100644 --- a/naga/src/valid/interface.rs +++ b/naga/src/valid/interface.rs @@ -309,7 +309,7 @@ impl VaryingContext<'_> { return Err(VaryingError::InvalidBuiltInStage(built_in)); } if !type_good { - log::warn!("Wrong builtin type: {:?}", ty_inner); + log::warn!("Wrong builtin type: {ty_inner:?}"); return Err(VaryingError::InvalidBuiltInType(built_in)); } } @@ -501,7 +501,7 @@ impl super::Validator { ) -> Result<(), GlobalVariableError> { use super::TypeFlags; - log::debug!("var {:?}", var); + log::debug!("var {var:?}"); let inner_ty = match gctx.types[var.ty].inner { // A binding array is (mostly) supposed to behave the same as a // series of individually bound resources, so we can (mostly) @@ -795,12 +795,8 @@ impl super::Validator { crate::AddressSpace::PushConstant => GlobalUse::READ, }; if !allowed_usage.contains(usage) { - log::warn!("\tUsage error for: {:?}", var); - log::warn!( - "\tAllowed usage: {:?}, requested: {:?}", - allowed_usage, - usage - ); + log::warn!("\tUsage error for: {var:?}"); + log::warn!("\tAllowed usage: {allowed_usage:?}, requested: {usage:?}"); return Err(EntryPointError::InvalidGlobalUsage(var_handle, usage) .with_span_handle(var_handle, &module.global_variables)); } diff --git a/naga/src/valid/mod.rs b/naga/src/valid/mod.rs index aef6a241646..426b3d637d7 100644 --- a/naga/src/valid/mod.rs +++ b/naga/src/valid/mod.rs @@ -35,6 +35,9 @@ pub use r#type::{Disalignment, PushConstantError, TypeError, TypeFlags, WidthErr use self::handles::InvalidHandleError; +/// Maximum size of a type, in bytes. +pub const MAX_TYPE_SIZE: u32 = 0x4000_0000; // 1GB + bitflags::bitflags! { /// Validation flags. /// @@ -128,13 +131,26 @@ bitflags::bitflags! { const CUBE_ARRAY_TEXTURES = 1 << 15; /// Support for 64-bit signed and unsigned integers. const SHADER_INT64 = 1 << 16; - /// Support for subgroup operations. - /// Implies support for subgroup operations in both fragment and compute stages, - /// but not necessarily in the vertex stage, which requires [`Capabilities::SUBGROUP_VERTEX_STAGE`]. + /// Support for subgroup operations (except barriers) in fragment and compute shaders. + /// + /// Subgroup operations in the vertex stage require + /// [`Capabilities::SUBGROUP_VERTEX_STAGE`] in addition to `Capabilities::SUBGROUP`. + /// (But note that `create_validator` automatically sets + /// `Capabilities::SUBGROUP` whenever `Features::SUBGROUP_VERTEX` is + /// available.) + /// + /// Subgroup barriers require [`Capabilities::SUBGROUP_BARRIER`] in addition to + /// `Capabilities::SUBGROUP`. const SUBGROUP = 1 << 17; - /// Support for subgroup barriers. + /// Support for subgroup barriers in compute shaders. + /// + /// Requires [`Capabilities::SUBGROUP`]. Without it, enables nothing. const SUBGROUP_BARRIER = 1 << 18; - /// Support for subgroup operations in the vertex stage. + /// Support for subgroup operations (not including barriers) in the vertex stage. + /// + /// Without [`Capabilities::SUBGROUP`], enables nothing. (But note that + /// `create_validator` automatically sets `Capabilities::SUBGROUP` + /// whenever `Features::SUBGROUP_VERTEX` is available.) const SUBGROUP_VERTEX_STAGE = 1 << 19; /// Support for [`AtomicFunction::Min`] and [`AtomicFunction::Max`] on /// 64-bit integers in the [`Storage`] address space, when the return @@ -165,6 +181,29 @@ bitflags::bitflags! { const RAY_HIT_VERTEX_POSITION = 1 << 25; /// Support for 16-bit floating-point types. const SHADER_FLOAT16 = 1 << 26; + /// Support for [`ImageClass::External`] + const TEXTURE_EXTERNAL = 1 << 27; + /// Support for `quantizeToF16`, `pack2x16float`, and `unpack2x16float`, which store + /// `f16`-precision values in `f32`s. + const SHADER_FLOAT16_IN_FLOAT32 = 1 << 28; + } +} + +impl Capabilities { + /// Returns the extension corresponding to this capability, if there is one. + /// + /// This is used by integration tests. + #[cfg(feature = "wgsl-in")] + #[doc(hidden)] + pub const fn extension(&self) -> Option { + use crate::front::wgsl::ImplementedEnableExtension as Ext; + match *self { + Self::DUAL_SOURCE_BLENDING => Some(Ext::DualSourceBlending), + // NOTE: `SHADER_FLOAT16_IN_FLOAT32` _does not_ require the `f16` extension + Self::SHADER_FLOAT16 => Some(Ext::F16), + Self::CLIP_DISTANCE => Some(Ext::ClipDistances), + _ => None, + } } } @@ -180,7 +219,11 @@ bitflags::bitflags! { #[cfg_attr(feature = "deserialize", derive(serde::Deserialize))] #[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] pub struct SubgroupOperationSet: u8 { - /// Elect, Barrier + /// Barriers + // Possibly elections, when that is supported. + // https://github.com/gfx-rs/wgpu/issues/6042#issuecomment-3272603431 + // Contrary to what the name "basic" suggests, HLSL/DX12 support the + // other subgroup operations, but do not support subgroup barriers. const BASIC = 1 << 0; /// Any, All const VOTE = 1 << 1; @@ -454,7 +497,19 @@ impl crate::TypeInner { } impl Validator { - /// Construct a new validator instance. + /// Create a validator for Naga [`Module`]s. + /// + /// The `flags` argument indicates which stages of validation the + /// returned `Validator` should perform. Skipping stages can make + /// validation somewhat faster, but the validator may not reject some + /// invalid modules. Regardless of `flags`, validation always returns + /// a usable [`ModuleInfo`] value on success. + /// + /// If `flags` contains everything in `ValidationFlags::default()`, + /// then the returned Naga [`Validator`] will reject any [`Module`] + /// that would use capabilities not included in `capabilities`. + /// + /// [`Module`]: crate::Module pub fn new(flags: ValidationFlags, capabilities: Capabilities) -> Self { let subgroup_operations = if capabilities.contains(Capabilities::SUBGROUP) { use SubgroupOperationSet as S; diff --git a/naga/src/valid/type.rs b/naga/src/valid/type.rs index b3ae13b7d4a..e8b83ff08f3 100644 --- a/naga/src/valid/type.rs +++ b/naga/src/valid/type.rs @@ -133,6 +133,8 @@ pub enum TypeError { InvalidDynamicArray(String, Handle), #[error("The base handle {0:?} has to be a struct")] BindingArrayBaseTypeNotStruct(Handle), + #[error("Binding arrays of external textures are not yet supported")] + BindingArrayBaseExternalTextures, #[error("Structure member[{index}] at {offset} overlaps the previous member")] MemberOverlap { index: u32, offset: u32 }, #[error( @@ -261,6 +263,15 @@ impl super::Validator { } } + /// Check whether `scalar` is a permitted scalar width. + /// + /// If `scalar` is not a width allowed by the selected [`Capabilities`], + /// return an error explaining why. + /// + /// If `scalar` is allowed, return a [`PushConstantCompatibility`] result + /// that says whether `scalar` is allowed specifically in push constants. + /// + /// [`Capabilities`]: crate::valid::Capabilities pub(super) const fn check_width( &self, scalar: crate::Scalar, @@ -732,6 +743,16 @@ impl super::Validator { if arrayed && matches!(dim, crate::ImageDimension::Cube) { self.require_type_capability(Capabilities::CUBE_ARRAY_TEXTURES)?; } + if matches!(class, crate::ImageClass::External) { + if dim != crate::ImageDimension::D2 || arrayed { + return Err(TypeError::UnsupportedImageType { + dim, + arrayed, + class, + }); + } + self.require_type_capability(Capabilities::TEXTURE_EXTERNAL)?; + } TypeInfo::new( TypeFlags::ARGUMENT | TypeFlags::CREATION_RESOLVED, Alignment::ONE, @@ -784,6 +805,17 @@ impl super::Validator { _ => return Err(TypeError::BindingArrayBaseTypeNotStruct(base)), }; } + if matches!( + gctx.types[base].inner, + crate::TypeInner::Image { + class: crate::ImageClass::External, + .. + } + ) { + // Binding arrays of external textures are not yet supported. + // https://github.com/gfx-rs/wgpu/issues/8027 + return Err(TypeError::BindingArrayBaseExternalTextures); + } if !base_info.flags.contains(TypeFlags::CREATION_RESOLVED) { return Err(TypeError::InvalidData(base)); diff --git a/naga/tests/in/glsl/bits_glsl.frag b/naga/tests/in/glsl/bits.frag similarity index 100% rename from naga/tests/in/glsl/bits_glsl.frag rename to naga/tests/in/glsl/bits.frag diff --git a/naga/tests/in/glsl/bits.toml b/naga/tests/in/glsl/bits.toml new file mode 100644 index 00000000000..96a1d507b4f --- /dev/null +++ b/naga/tests/in/glsl/bits.toml @@ -0,0 +1,2 @@ +# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `F16_IN_F32` capability +god_mode = true diff --git a/naga/tests/in/glsl/dual-source-blending.toml b/naga/tests/in/glsl/dual-source-blending.toml index c3c2d8aa9b0..a51f548feec 100644 --- a/naga/tests/in/glsl/dual-source-blending.toml +++ b/naga/tests/in/glsl/dual-source-blending.toml @@ -1 +1,2 @@ -god_mode = true # TODO: replace with finer `DUAL_SOURCE_BLENDING` capability +# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `DUAL_SOURCE_BLENDING` capability +god_mode = true diff --git a/naga/tests/in/spv/8151-barrier-reorder.spvasm b/naga/tests/in/spv/8151-barrier-reorder.spvasm new file mode 100644 index 00000000000..733e4d74414 --- /dev/null +++ b/naga/tests/in/spv/8151-barrier-reorder.spvasm @@ -0,0 +1,63 @@ +; SPIR-V +; Version: 1.3 +; Generator: Google rspirv; 0 +; Bound: 45 +; Schema: 0 + OpCapability Shader + OpCapability VulkanMemoryModel + OpExtension "SPV_KHR_vulkan_memory_model" + OpMemoryModel Logical Vulkan + OpEntryPoint GLCompute %1 "barrier_reorder_bug" %gl_LocalInvocationID + OpExecutionMode %1 LocalSize 2 1 1 + OpDecorate %gl_LocalInvocationID BuiltIn LocalInvocationId + OpDecorate %_runtimearr_uint ArrayStride 4 + OpDecorate %_struct_8 Block + OpMemberDecorate %_struct_8 0 Offset 0 + OpDecorate %5 Binding 0 + OpDecorate %5 DescriptorSet 0 + %uint = OpTypeInt 32 0 + %v3uint = OpTypeVector %uint 3 +%_ptr_Input_v3uint = OpTypePointer Input %v3uint + %void = OpTypeVoid + %13 = OpTypeFunction %void +%gl_LocalInvocationID = OpVariable %_ptr_Input_v3uint Input +%_runtimearr_uint = OpTypeRuntimeArray %uint + %_struct_8 = OpTypeStruct %_runtimearr_uint +%_ptr_StorageBuffer__struct_8 = OpTypePointer StorageBuffer %_struct_8 + %5 = OpVariable %_ptr_StorageBuffer__struct_8 StorageBuffer + %uint_0 = OpConstant %uint 0 + %bool = OpTypeBool +%_ptr_Workgroup_uint = OpTypePointer Workgroup %uint + %6 = OpVariable %_ptr_Workgroup_uint Workgroup + %uint_1 = OpConstant %uint 1 + %uint_2 = OpConstant %uint 2 + %uint_264 = OpConstant %uint 264 +%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint + %1 = OpFunction %void None %13 + %23 = OpLabel + %24 = OpLoad %v3uint %gl_LocalInvocationID + %27 = OpCompositeExtract %uint %24 0 + %28 = OpIEqual %bool %27 %uint_0 + OpSelectionMerge %29 None + OpBranchConditional %28 %30 %31 + %30 = OpLabel + OpStore %6 %uint_1 + OpBranch %29 + %31 = OpLabel + OpBranch %29 + %29 = OpLabel + OpControlBarrier %uint_2 %uint_2 %uint_264 + %32 = OpLoad %uint %6 + OpControlBarrier %uint_2 %uint_2 %uint_264 + %39 = OpInBoundsAccessChain %_ptr_StorageBuffer_uint %5 %uint_0 %27 + OpStore %39 %32 + OpSelectionMerge %42 None + OpBranchConditional %28 %43 %44 + %43 = OpLabel + OpStore %6 %uint_2 + OpBranch %42 + %44 = OpLabel + OpBranch %42 + %42 = OpLabel + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/binding-arrays.runtime.slang b/naga/tests/in/spv/binding-arrays.runtime.slang new file mode 100644 index 00000000000..6230fc9ced9 --- /dev/null +++ b/naga/tests/in/spv/binding-arrays.runtime.slang @@ -0,0 +1,19 @@ +// Compiled with: +// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/binding-arrays.runtime.spv naga/tests/in/spv/binding-arrays.runtime.slang +// Disassembled with: +// spirv-dis naga/tests/in/spv/binding-arrays.runtime.spv -o naga/tests/in/spv/binding-arrays.runtime.spvasm + +#language slang 2026 + +[[vk::binding(0, 0)]] var textures: Texture2D[]; +[[vk::binding(1, 0)]] var linear_sampler: SamplerState; + +struct VertexOutput { + var texture_coordinates: float2; + var texture_index: uint; +}; + +[[shader("pixel")]] +func main(input: VertexOutput) -> float4 { + return textures[input.texture_index].Sample(linear_sampler, input.texture_coordinates); +} diff --git a/naga/tests/in/spv/binding-arrays.runtime.spvasm b/naga/tests/in/spv/binding-arrays.runtime.spvasm new file mode 100644 index 00000000000..41fc2db6d62 --- /dev/null +++ b/naga/tests/in/spv/binding-arrays.runtime.spvasm @@ -0,0 +1,60 @@ +; SPIR-V +; Version: 1.5 +; Generator: Khronos Slang Compiler; 0 +; Bound: 33 +; Schema: 0 + OpCapability RuntimeDescriptorArray + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %textures %linear_sampler %entryPointParam_main %input_texture_coordinates %input_texture_index + OpExecutionMode %main OriginUpperLeft + OpSource Slang 1 + OpName %input_texture_coordinates "input.texture_coordinates" + OpName %input_texture_index "input.texture_index" + OpName %textures "textures" + OpName %linear_sampler "linear_sampler" + OpName %sampledImage "sampledImage" + OpName %sampled "sampled" + OpName %entryPointParam_main "entryPointParam_main" + OpName %main "main" + OpDecorate %input_texture_coordinates Location 0 + OpDecorate %input_texture_index Location 1 + OpDecorate %input_texture_index Flat + OpDecorate %textures Binding 0 + OpDecorate %textures DescriptorSet 0 + OpDecorate %linear_sampler Binding 1 + OpDecorate %linear_sampler DescriptorSet 0 + OpDecorate %entryPointParam_main Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %uint = OpTypeInt 32 0 +%_ptr_Input_uint = OpTypePointer Input %uint + %15 = OpTypeImage %float 2D 2 0 0 1 Unknown +%_runtimearr_15 = OpTypeRuntimeArray %15 +%_ptr_UniformConstant__runtimearr_15 = OpTypePointer UniformConstant %_runtimearr_15 +%_ptr_UniformConstant_15 = OpTypePointer UniformConstant %15 + %22 = OpTypeSampler +%_ptr_UniformConstant_22 = OpTypePointer UniformConstant %22 + %26 = OpTypeSampledImage %15 + %v4float = OpTypeVector %float 4 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input +%input_texture_index = OpVariable %_ptr_Input_uint Input + %textures = OpVariable %_ptr_UniformConstant__runtimearr_15 UniformConstant +%linear_sampler = OpVariable %_ptr_UniformConstant_22 UniformConstant +%entryPointParam_main = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %4 = OpLabel + %7 = OpLoad %v2float %input_texture_coordinates + %11 = OpLoad %uint %input_texture_index + %19 = OpAccessChain %_ptr_UniformConstant_15 %textures %11 + %21 = OpLoad %15 %19 + %23 = OpLoad %22 %linear_sampler +%sampledImage = OpSampledImage %26 %21 %23 + %sampled = OpImageSampleImplicitLod %v4float %sampledImage %7 None + OpStore %entryPointParam_main %sampled + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/binding-arrays.runtime.toml b/naga/tests/in/spv/binding-arrays.runtime.toml new file mode 100644 index 00000000000..fd5664c637a --- /dev/null +++ b/naga/tests/in/spv/binding-arrays.runtime.toml @@ -0,0 +1,4 @@ +god_mode = true + +[spv-in] +adjust_coordinate_space = true diff --git a/naga/tests/in/spv/gather-cmp.slang b/naga/tests/in/spv/gather-cmp.slang new file mode 100644 index 00000000000..6d579d38f5a --- /dev/null +++ b/naga/tests/in/spv/gather-cmp.slang @@ -0,0 +1,18 @@ +// Compiled with: +// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/gather-cmp.spv naga/tests/in/spv/gather-cmp.slang +// Disassembled with: +// spirv-dis naga/tests/in/spv/gather-cmp.spv -o naga/tests/in/spv/gather-cmp.spvasm + +#language slang 2026 + +[[vk::binding(0, 0)]] var texture: Texture2D; +[[vk::binding(1, 0)]] var depth_sampler: SamplerComparisonState; + +struct VertexOutput { + var texture_coordinates: float2; +}; + +[[shader("pixel")]] +func main(input: VertexOutput) -> float4 { + return texture.GatherCmp(depth_sampler, input.texture_coordinates, 0.5); +} diff --git a/naga/tests/in/spv/gather-cmp.spvasm b/naga/tests/in/spv/gather-cmp.spvasm new file mode 100644 index 00000000000..39e8f819b13 --- /dev/null +++ b/naga/tests/in/spv/gather-cmp.spvasm @@ -0,0 +1,49 @@ +; SPIR-V +; Version: 1.5 +; Generator: Khronos Slang Compiler; 0 +; Bound: 27 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %texture %depth_sampler %entryPointParam_main %input_texture_coordinates + OpExecutionMode %main OriginUpperLeft + OpSource Slang 1 + OpName %input_texture_coordinates "input.texture_coordinates" + OpName %texture "texture" + OpName %depth_sampler "depth_sampler" + OpName %sampledImage "sampledImage" + OpName %entryPointParam_main "entryPointParam_main" + OpName %main "main" + OpDecorate %input_texture_coordinates Location 0 + OpDecorate %texture Binding 0 + OpDecorate %texture DescriptorSet 0 + OpDecorate %depth_sampler Binding 1 + OpDecorate %depth_sampler DescriptorSet 0 + OpDecorate %entryPointParam_main Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %10 = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %18 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %float_0_5 = OpConstant %float 0.5 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input + %texture = OpVariable %_ptr_UniformConstant_10 UniformConstant +%depth_sampler = OpVariable %_ptr_UniformConstant_14 UniformConstant +%entryPointParam_main = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %4 = OpLabel + %7 = OpLoad %v2float %input_texture_coordinates + %11 = OpLoad %10 %texture + %15 = OpLoad %14 %depth_sampler +%sampledImage = OpSampledImage %18 %11 %15 + %21 = OpImageDrefGather %v4float %sampledImage %7 %float_0_5 + OpStore %entryPointParam_main %21 + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/gather-cmp.toml b/naga/tests/in/spv/gather-cmp.toml new file mode 100644 index 00000000000..fd5664c637a --- /dev/null +++ b/naga/tests/in/spv/gather-cmp.toml @@ -0,0 +1,4 @@ +god_mode = true + +[spv-in] +adjust_coordinate_space = true diff --git a/naga/tests/in/spv/gather.slang b/naga/tests/in/spv/gather.slang new file mode 100644 index 00000000000..20b1a21c96a --- /dev/null +++ b/naga/tests/in/spv/gather.slang @@ -0,0 +1,18 @@ +// Compiled with: +// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/gather.spv naga/tests/in/spv/gather.slang +// Disassembled with: +// spirv-dis naga/tests/in/spv/gather.spv -o naga/tests/in/spv/gather.spvasm + +#language slang 2026 + +[[vk::binding(0, 0)]] var texture: Texture2D; +[[vk::binding(1, 0)]] var linear_sampler: SamplerState; + +struct VertexOutput { + var texture_coordinates: float2; +}; + +[[shader("pixel")]] +func main(input: VertexOutput) -> float4 { + return texture.GatherGreen(linear_sampler, input.texture_coordinates); +} diff --git a/naga/tests/in/spv/gather.spvasm b/naga/tests/in/spv/gather.spvasm new file mode 100644 index 00000000000..65a8946d05f --- /dev/null +++ b/naga/tests/in/spv/gather.spvasm @@ -0,0 +1,50 @@ +; SPIR-V +; Version: 1.5 +; Generator: Khronos Slang Compiler; 0 +; Bound: 28 +; Schema: 0 + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %main "main" %texture %linear_sampler %entryPointParam_main %input_texture_coordinates + OpExecutionMode %main OriginUpperLeft + OpSource Slang 1 + OpName %input_texture_coordinates "input.texture_coordinates" + OpName %texture "texture" + OpName %linear_sampler "linear_sampler" + OpName %sampledImage "sampledImage" + OpName %entryPointParam_main "entryPointParam_main" + OpName %main "main" + OpDecorate %input_texture_coordinates Location 0 + OpDecorate %texture Binding 0 + OpDecorate %texture DescriptorSet 0 + OpDecorate %linear_sampler Binding 1 + OpDecorate %linear_sampler DescriptorSet 0 + OpDecorate %entryPointParam_main Location 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %float = OpTypeFloat 32 + %v2float = OpTypeVector %float 2 +%_ptr_Input_v2float = OpTypePointer Input %v2float + %10 = OpTypeImage %float 2D 2 0 0 1 Unknown +%_ptr_UniformConstant_10 = OpTypePointer UniformConstant %10 + %14 = OpTypeSampler +%_ptr_UniformConstant_14 = OpTypePointer UniformConstant %14 + %18 = OpTypeSampledImage %10 + %v4float = OpTypeVector %float 4 + %int = OpTypeInt 32 1 + %int_1 = OpConstant %int 1 +%_ptr_Output_v4float = OpTypePointer Output %v4float +%input_texture_coordinates = OpVariable %_ptr_Input_v2float Input + %texture = OpVariable %_ptr_UniformConstant_10 UniformConstant +%linear_sampler = OpVariable %_ptr_UniformConstant_14 UniformConstant +%entryPointParam_main = OpVariable %_ptr_Output_v4float Output + %main = OpFunction %void None %3 + %4 = OpLabel + %7 = OpLoad %v2float %input_texture_coordinates + %11 = OpLoad %10 %texture + %15 = OpLoad %14 %linear_sampler +%sampledImage = OpSampledImage %18 %11 %15 + %21 = OpImageGather %v4float %sampledImage %7 %int_1 + OpStore %entryPointParam_main %21 + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/gather.toml b/naga/tests/in/spv/gather.toml new file mode 100644 index 00000000000..fd5664c637a --- /dev/null +++ b/naga/tests/in/spv/gather.toml @@ -0,0 +1,4 @@ +god_mode = true + +[spv-in] +adjust_coordinate_space = true diff --git a/naga/tests/in/spv/load-ms-texture.slang b/naga/tests/in/spv/load-ms-texture.slang new file mode 100644 index 00000000000..d8f80fd4e8d --- /dev/null +++ b/naga/tests/in/spv/load-ms-texture.slang @@ -0,0 +1,21 @@ +// Compiled with: +// slangc -target spirv -profile spirv_1_5 -o naga/tests/in/spv/load-ms-texture.spv naga/tests/in/spv/load-ms-texture.slang +// Disassembled with: +// spirv-dis naga/tests/in/spv/load-ms-texture.spv -o naga/tests/in/spv/load-ms-texture.spvasm +#language slang 2026 + +[[vk::binding(0, 0)]] var texture: Texture2DMS; + +[[shader("pixel")]] +func fs_main(float4 position : SV_Position) -> float4 { + let pixel_coord = int2(position.xy); + var color: float4; + + for (var index: int = 0; index < 8; index++) { + color += texture.Load(pixel_coord, index); + } + + color = color / 8.0; + + return color; +} diff --git a/naga/tests/in/spv/load-ms-texture.spvasm b/naga/tests/in/spv/load-ms-texture.spvasm new file mode 100644 index 00000000000..28d4e3d691e --- /dev/null +++ b/naga/tests/in/spv/load-ms-texture.spvasm @@ -0,0 +1,93 @@ +; SPIR-V +; Version: 1.5 +; Generator: Khronos Slang Compiler; 0 +; Bound: 65 +; Schema: 0 + OpCapability StorageImageMultisample + OpCapability Shader + OpMemoryModel Logical GLSL450 + OpEntryPoint Fragment %fs_main "main" %texture %entryPointParam_fs_main %gl_FragCoord + OpExecutionMode %fs_main OriginUpperLeft + OpSource Slang 1 + OpName %index "index" + OpName %index "index" + OpName %color "color" + OpName %color "color" + OpName %color_0 "color" + OpName %entryPointParam_fs_main "entryPointParam_fs_main" + OpName %texture "texture" + OpName %sampled "sampled" + OpName %color_1 "color" + OpName %fs_main "fs_main" + OpDecorate %gl_FragCoord BuiltIn FragCoord + OpDecorate %entryPointParam_fs_main Location 0 + OpDecorate %texture Binding 0 + OpDecorate %texture DescriptorSet 0 + %void = OpTypeVoid + %3 = OpTypeFunction %void + %int = OpTypeInt 32 1 +%_ptr_Function_int = OpTypePointer Function %int + %float = OpTypeFloat 32 + %v4float = OpTypeVector %float 4 +%_ptr_Function_v4float = OpTypePointer Function %v4float +%_ptr_Input_v4float = OpTypePointer Input %v4float + %v2float = OpTypeVector %float 2 + %v2int = OpTypeVector %int 2 + %int_0 = OpConstant %int 0 + %bool = OpTypeBool + %int_8 = OpConstant %int 8 +%_ptr_Output_v4float = OpTypePointer Output %v4float + %45 = OpTypeImage %float 2D 2 0 1 1 Unknown +%_ptr_UniformConstant_45 = OpTypePointer UniformConstant %45 + %int_1 = OpConstant %int 1 +%gl_FragCoord = OpVariable %_ptr_Input_v4float Input +%entryPointParam_fs_main = OpVariable %_ptr_Output_v4float Output + %texture = OpVariable %_ptr_UniformConstant_45 UniformConstant +%float_0_125 = OpConstant %float 0.125 + %64 = OpConstantComposite %v4float %float_0_125 %float_0_125 %float_0_125 %float_0_125 + %fs_main = OpFunction %void None %3 + %4 = OpLabel + %index = OpVariable %_ptr_Function_int Function + %color = OpVariable %_ptr_Function_v4float Function + %22 = OpLoad %v4float %gl_FragCoord + %26 = OpVectorShuffle %v2float %22 %22 0 1 + %28 = OpConvertFToS %v2int %26 + OpStore %index %int_0 + OpBranch %12 + %12 = OpLabel + OpLoopMerge %17 %21 None + OpBranch %13 + %13 = OpLabel + OpBranch %14 + %14 = OpLabel + OpBranch %15 + %15 = OpLabel + %31 = OpLoad %int %index + %33 = OpSLessThan %bool %31 %int_8 + OpSelectionMerge %18 None + OpBranchConditional %33 %18 %16 + %16 = OpLabel + OpBranch %17 + %18 = OpLabel + %46 = OpLoad %45 %texture + %49 = OpLoad %int %index + %sampled = OpImageFetch %v4float %46 %28 Sample %49 + %52 = OpLoad %v4float %color + %color_1 = OpFAdd %v4float %52 %sampled + OpBranch %19 + %19 = OpLabel + OpBranch %20 + %20 = OpLabel + %56 = OpLoad %int %index + %57 = OpIAdd %int %56 %int_1 + OpStore %index %57 + OpStore %color %color_1 + OpBranch %21 + %21 = OpLabel + OpBranch %12 + %17 = OpLabel + %37 = OpLoad %v4float %color + %color_0 = OpFMul %v4float %37 %64 + OpStore %entryPointParam_fs_main %color_0 + OpReturn + OpFunctionEnd diff --git a/naga/tests/in/spv/load-ms-texture.toml b/naga/tests/in/spv/load-ms-texture.toml new file mode 100644 index 00000000000..fd5664c637a --- /dev/null +++ b/naga/tests/in/spv/load-ms-texture.toml @@ -0,0 +1,4 @@ +god_mode = true + +[spv-in] +adjust_coordinate_space = true diff --git a/naga/tests/in/wgsl/7995-unicode-idents.wgsl b/naga/tests/in/wgsl/7995-unicode-idents.wgsl new file mode 100644 index 00000000000..64e932ed09d --- /dev/null +++ b/naga/tests/in/wgsl/7995-unicode-idents.wgsl @@ -0,0 +1,14 @@ +// NOTE: This allows us to suppress compaction below, to force the handling of identifiers +// containing Unicode. +@group(0) @binding(0) +var asdf: f32; + +fn compute() -> f32 { + let θ2 = asdf + 9001.0; + return θ2; +} + +@compute @workgroup_size(1, 1) +fn main() { + compute(); +} diff --git a/naga/tests/in/wgsl/access.wgsl b/naga/tests/in/wgsl/access.wgsl index d93f2b30a10..0e89bcfb0eb 100644 --- a/naga/tests/in/wgsl/access.wgsl +++ b/naga/tests/in/wgsl/access.wgsl @@ -35,6 +35,9 @@ var baz: Baz; var qux: vec2; fn test_matrix_within_struct_accesses() { + // Test HLSL accesses to Cx2 matrices. There are additional tests + // in `hlsl_mat_cx2.wgsl`. + var idx = 1; idx--; diff --git a/naga/tests/in/wgsl/binding-arrays.toml b/naga/tests/in/wgsl/binding-arrays.toml index 27c15064171..a76141a6727 100644 --- a/naga/tests/in/wgsl/binding-arrays.toml +++ b/naga/tests/in/wgsl/binding-arrays.toml @@ -62,5 +62,5 @@ resource_binding = { group = 0, binding = 8 } version = [1, 1] [[spv.binding_map]] -bind_target = { binding_array_size = 10 } +bind_target = { descriptor_set = 0, binding = 0, binding_array_size = 10 } resource_binding = { group = 0, binding = 0 } diff --git a/naga/tests/in/wgsl/binding-buffer-arrays.toml b/naga/tests/in/wgsl/binding-buffer-arrays.toml index be1b09cf10d..7532299cb76 100644 --- a/naga/tests/in/wgsl/binding-buffer-arrays.toml +++ b/naga/tests/in/wgsl/binding-buffer-arrays.toml @@ -9,5 +9,5 @@ image = "ReadZeroSkipWrite" version = [1, 1] [[spv.binding_map]] -bind_target = { binding_array_size = 10 } +bind_target = { descriptor_set = 0, binding = 0, binding_array_size = 10 } resource_binding = { group = 0, binding = 0 } diff --git a/naga/tests/in/wgsl/bits-optimized-msl.toml b/naga/tests/in/wgsl/bits-optimized-msl.toml index 9409d2ac77c..7dbe8fcbf00 100644 --- a/naga/tests/in/wgsl/bits-optimized-msl.toml +++ b/naga/tests/in/wgsl/bits-optimized-msl.toml @@ -1,3 +1,5 @@ +# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `F16_IN_F32` capability +god_mode = true targets = "METAL" [msl] diff --git a/naga/tests/in/wgsl/bits.toml b/naga/tests/in/wgsl/bits.toml index 2751860cfe1..7d254eb02f2 100644 --- a/naga/tests/in/wgsl/bits.toml +++ b/naga/tests/in/wgsl/bits.toml @@ -1,3 +1,6 @@ +# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `F16_IN_F32` capability +god_mode = true + [msl] fake_missing_bindings = false lang_version = [1, 2] diff --git a/naga/tests/in/wgsl/control-flow.wgsl b/naga/tests/in/wgsl/control-flow.wgsl index d8e3fb8f588..932eecd4c79 100644 --- a/naga/tests/in/wgsl/control-flow.wgsl +++ b/naga/tests/in/wgsl/control-flow.wgsl @@ -58,6 +58,25 @@ fn control_flow() { pos = 3; } } + + // trailing commas + switch pos { + case 1, { + pos = 0; + } + case 2,: { + pos = 1; + } + case 3, 4, { + pos = 2; + } + case 5, 6,: { + pos = 3; + } + default { + pos = 4; + } + } } fn switch_default_break(i: i32) { diff --git a/naga/tests/in/wgsl/empty-if.toml b/naga/tests/in/wgsl/empty-if.toml new file mode 100644 index 00000000000..b7d70bcf51f --- /dev/null +++ b/naga/tests/in/wgsl/empty-if.toml @@ -0,0 +1,2 @@ +[spv] +version = [1, 6] diff --git a/naga/tests/in/wgsl/empty-if.wgsl b/naga/tests/in/wgsl/empty-if.wgsl new file mode 100644 index 00000000000..c72eb7a0128 --- /dev/null +++ b/naga/tests/in/wgsl/empty-if.wgsl @@ -0,0 +1,9 @@ +@workgroup_size(1) +@compute +fn comp(@builtin(global_invocation_id) id: vec3) { + if (id.x == 0) { + + } + _ = 1+1; // otherwise, naga generates returns in the if statement. + return; +} \ No newline at end of file diff --git a/naga/tests/in/wgsl/f16-native.toml b/naga/tests/in/wgsl/f16-native.toml new file mode 100644 index 00000000000..529d34f80da --- /dev/null +++ b/naga/tests/in/wgsl/f16-native.toml @@ -0,0 +1,13 @@ +targets = "SPIRV" +god_mode = true + +[spv] +debug = true +version = [1, 1] +use_storage_input_output_16 = true +capabilities = ["Float16"] + +[bounds_check_policies] +index = "ReadZeroSkipWrite" +buffer = "ReadZeroSkipWrite" +image = "ReadZeroSkipWrite" diff --git a/naga/tests/in/wgsl/f16-native.wgsl b/naga/tests/in/wgsl/f16-native.wgsl new file mode 100644 index 00000000000..fda726df765 --- /dev/null +++ b/naga/tests/in/wgsl/f16-native.wgsl @@ -0,0 +1,79 @@ +enable f16; + +@fragment +fn test_direct( + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +) -> F16IO { + var output: F16IO; + output.scalar_f16 = scalar_f16 + 1.0h; + output.scalar_f32 = scalar_f32 + 1.0; + output.vec2_f16 = vec2_f16 + vec2(1.0h); + output.vec2_f32 = vec2_f32 + vec2(1.0); + output.vec3_f16 = vec3_f16 + vec3(1.0h); + output.vec3_f32 = vec3_f32 + vec3(1.0); + output.vec4_f16 = vec4_f16 + vec4(1.0h); + output.vec4_f32 = vec4_f32 + vec4(1.0); + return output; +} + +struct F16IO { + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +} + +@fragment +fn test_struct(input: F16IO) -> F16IO { + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_copy_input(input_original: F16IO) -> F16IO { + var input = input_original; + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_return_partial(input_original: F16IO) -> @location(0) f16 { + var input = input_original; + input.scalar_f16 = 0.0h; + return input.scalar_f16; +} + +@fragment +fn test_component_access(input: F16IO) -> F16IO { + var output: F16IO; + output.vec2_f16.x = input.vec2_f16.y; + output.vec2_f16.y = input.vec2_f16.x; + return output; +} \ No newline at end of file diff --git a/naga/tests/in/wgsl/f16-polyfill.toml b/naga/tests/in/wgsl/f16-polyfill.toml new file mode 100644 index 00000000000..96160063e05 --- /dev/null +++ b/naga/tests/in/wgsl/f16-polyfill.toml @@ -0,0 +1,13 @@ +targets = "SPIRV" +god_mode = true + +[spv] +debug = true +version = [1, 1] +use_storage_input_output_16 = false +capabilities = ["Float16"] + +[bounds_check_policies] +index = "ReadZeroSkipWrite" +buffer = "ReadZeroSkipWrite" +image = "ReadZeroSkipWrite" diff --git a/naga/tests/in/wgsl/f16-polyfill.wgsl b/naga/tests/in/wgsl/f16-polyfill.wgsl new file mode 100644 index 00000000000..fda726df765 --- /dev/null +++ b/naga/tests/in/wgsl/f16-polyfill.wgsl @@ -0,0 +1,79 @@ +enable f16; + +@fragment +fn test_direct( + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +) -> F16IO { + var output: F16IO; + output.scalar_f16 = scalar_f16 + 1.0h; + output.scalar_f32 = scalar_f32 + 1.0; + output.vec2_f16 = vec2_f16 + vec2(1.0h); + output.vec2_f32 = vec2_f32 + vec2(1.0); + output.vec3_f16 = vec3_f16 + vec3(1.0h); + output.vec3_f32 = vec3_f32 + vec3(1.0); + output.vec4_f16 = vec4_f16 + vec4(1.0h); + output.vec4_f32 = vec4_f32 + vec4(1.0); + return output; +} + +struct F16IO { + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +} + +@fragment +fn test_struct(input: F16IO) -> F16IO { + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_copy_input(input_original: F16IO) -> F16IO { + var input = input_original; + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_return_partial(input_original: F16IO) -> @location(0) f16 { + var input = input_original; + input.scalar_f16 = 0.0h; + return input.scalar_f16; +} + +@fragment +fn test_component_access(input: F16IO) -> F16IO { + var output: F16IO; + output.vec2_f16.x = input.vec2_f16.y; + output.vec2_f16.y = input.vec2_f16.x; + return output; +} \ No newline at end of file diff --git a/naga/tests/in/wgsl/hlsl_mat_cx2.toml b/naga/tests/in/wgsl/hlsl_mat_cx2.toml new file mode 100644 index 00000000000..3ca0b52f4e5 --- /dev/null +++ b/naga/tests/in/wgsl/hlsl_mat_cx2.toml @@ -0,0 +1 @@ +targets = "HLSL" diff --git a/naga/tests/in/wgsl/hlsl_mat_cx2.wgsl b/naga/tests/in/wgsl/hlsl_mat_cx2.wgsl new file mode 100644 index 00000000000..50bc188794a --- /dev/null +++ b/naga/tests/in/wgsl/hlsl_mat_cx2.wgsl @@ -0,0 +1,177 @@ +// Test HLSL handling of N-by-2 matrices. +// See the doc comment on `naga::back::hlsl` for details. +// +// There are additional tests in `access.wgsl`. +// +// Tests that we don't apply this handling to other sizes are in hlsl_mat_cx3.wgsl. + +// Access type (3rd item in variable names) +// S = Struct +// M = Matrix +// C = Column +// E = Element + +// Index type (4th item in variable names) +// C = Constant +// V = Variable + +alias Mat = mat2x2; + +@group(0) @binding(0) +var s_m: Mat; + +@group(0) @binding(1) +var u_m: Mat; + +fn access_m() { + var idx = 1; + idx--; + + // loads from storage + let l_s_m = s_m; + let l_s_c_c = s_m[0]; + let l_s_c_v = s_m[idx]; + let l_s_e_cc = s_m[0][0]; + let l_s_e_cv = s_m[0][idx]; + let l_s_e_vc = s_m[idx][0]; + let l_s_e_vv = s_m[idx][idx]; + + // loads from uniform + let l_u_m = u_m; + let l_u_c_c = u_m[0]; + let l_u_c_v = u_m[idx]; + let l_u_e_cc = u_m[0][0]; + let l_u_e_cv = u_m[0][idx]; + let l_u_e_vc = u_m[idx][0]; + let l_u_e_vv = u_m[idx][idx]; + + // stores to storage + s_m = l_u_m; + s_m[0] = l_u_c_c; + s_m[idx] = l_u_c_v; + s_m[0][0] = l_u_e_cc; + s_m[0][idx] = l_u_e_cv; + s_m[idx][0] = l_u_e_vc; + s_m[idx][idx] = l_u_e_vv; +} + +struct StructWithMat { + m: Mat, +} + +@group(1) @binding(0) +var s_sm: StructWithMat; + +@group(1) @binding(1) +var u_sm: StructWithMat; + +fn access_sm() { + var idx = 1; + idx--; + + // loads from storage + let l_s_s = s_sm; + let l_s_m = s_sm.m; + let l_s_c_c = s_sm.m[0]; + let l_s_c_v = s_sm.m[idx]; + let l_s_e_cc = s_sm.m[0][0]; + let l_s_e_cv = s_sm.m[0][idx]; + let l_s_e_vc = s_sm.m[idx][0]; + let l_s_e_vv = s_sm.m[idx][idx]; + + // loads from uniform + let l_u_s = u_sm; + let l_u_m = u_sm.m; + let l_u_c_c = u_sm.m[0]; + let l_u_c_v = u_sm.m[idx]; + let l_u_e_cc = u_sm.m[0][0]; + let l_u_e_cv = u_sm.m[0][idx]; + let l_u_e_vc = u_sm.m[idx][0]; + let l_u_e_vv = u_sm.m[idx][idx]; + + // stores to storage + s_sm = l_u_s; + s_sm.m = l_u_m; + s_sm.m[0] = l_u_c_c; + s_sm.m[idx] = l_u_c_v; + s_sm.m[0][0] = l_u_e_cc; + s_sm.m[0][idx] = l_u_e_cv; + s_sm.m[idx][0] = l_u_e_vc; + s_sm.m[idx][idx] = l_u_e_vv; +} + +struct StructWithArrayOfStructOfMat { + a: array, +} + +@group(2) @binding(0) +var s_sasm: StructWithArrayOfStructOfMat; + +@group(2) @binding(1) +var u_sasm: StructWithArrayOfStructOfMat; + +fn access_sasm() { + var idx = 1; + idx--; + + // loads from storage + let l_s_s = s_sasm; + let l_s_a = s_sasm.a; + let l_s_m_c = s_sasm.a[0].m; + let l_s_m_v = s_sasm.a[idx].m; + let l_s_c_cc = s_sasm.a[0].m[0]; + let l_s_c_cv = s_sasm.a[0].m[idx]; + let l_s_c_vc = s_sasm.a[idx].m[0]; + let l_s_c_vv = s_sasm.a[idx].m[idx]; + let l_s_e_ccc = s_sasm.a[0].m[0][0]; + let l_s_e_ccv = s_sasm.a[0].m[0][idx]; + let l_s_e_cvc = s_sasm.a[0].m[idx][0]; + let l_s_e_cvv = s_sasm.a[0].m[idx][idx]; + let l_s_e_vcc = s_sasm.a[idx].m[0][0]; + let l_s_e_vcv = s_sasm.a[idx].m[0][idx]; + let l_s_e_vvc = s_sasm.a[idx].m[idx][0]; + let l_s_e_vvv = s_sasm.a[idx].m[idx][idx]; + + // loads from uniform + let l_u_s = u_sasm; + let l_u_a = u_sasm.a; + let l_u_m_c = u_sasm.a[0].m; + let l_u_m_v = u_sasm.a[idx].m; + let l_u_c_cc = u_sasm.a[0].m[0]; + let l_u_c_cv = u_sasm.a[0].m[idx]; + let l_u_c_vc = u_sasm.a[idx].m[0]; + let l_u_c_vv = u_sasm.a[idx].m[idx]; + let l_u_e_ccc = u_sasm.a[0].m[0][0]; + let l_u_e_ccv = u_sasm.a[0].m[0][idx]; + let l_u_e_cvc = u_sasm.a[0].m[idx][0]; + let l_u_e_cvv = u_sasm.a[0].m[idx][idx]; + let l_u_e_vcc = u_sasm.a[idx].m[0][0]; + let l_u_e_vcv = u_sasm.a[idx].m[0][idx]; + let l_u_e_vvc = u_sasm.a[idx].m[idx][0]; + let l_u_e_vvv = u_sasm.a[idx].m[idx][idx]; + + // stores to storage + s_sasm = l_u_s; + s_sasm.a = l_u_a; + s_sasm.a[0].m = l_u_m_c; + s_sasm.a[idx].m = l_u_m_v; + s_sasm.a[0].m[0] = l_u_c_cc; + s_sasm.a[0].m[idx] = l_u_c_cv; + s_sasm.a[idx].m[0] = l_u_c_vc; + s_sasm.a[idx].m[idx] = l_u_c_vv; + s_sasm.a[0].m[0][0] = l_u_e_ccc; + s_sasm.a[0].m[0][idx] = l_u_e_ccv; + s_sasm.a[0].m[idx][0] = l_u_e_cvc; + s_sasm.a[0].m[idx][idx] = l_u_e_cvv; + s_sasm.a[idx].m[0][0] = l_u_e_vcc; + s_sasm.a[idx].m[0][idx] = l_u_e_vcv; + s_sasm.a[idx].m[idx][0] = l_u_e_vvc; + s_sasm.a[idx].m[idx][idx] = l_u_e_vvv; +} + +@compute @workgroup_size(1) +fn main() { + access_m(); + access_sm(); + access_sasm(); +} diff --git a/naga/tests/in/wgsl/hlsl_mat_cx3.toml b/naga/tests/in/wgsl/hlsl_mat_cx3.toml new file mode 100644 index 00000000000..3ca0b52f4e5 --- /dev/null +++ b/naga/tests/in/wgsl/hlsl_mat_cx3.toml @@ -0,0 +1 @@ +targets = "HLSL" diff --git a/naga/tests/in/wgsl/hlsl_mat_cx3.wgsl b/naga/tests/in/wgsl/hlsl_mat_cx3.wgsl new file mode 100644 index 00000000000..e33f10fc9c5 --- /dev/null +++ b/naga/tests/in/wgsl/hlsl_mat_cx3.wgsl @@ -0,0 +1,173 @@ +// Test HLSL handling of N-by-3 matrices. These should not receive the special +// treatment that N-by-2 matrices receive (which is tested in hlsl_mat_cx2). + +// Access type (3rd item in variable names) +// S = Struct +// M = Matrix +// C = Column +// E = Element + +// Index type (4th item in variable names) +// C = Constant +// V = Variable + +alias Mat = mat3x3; + +@group(0) @binding(0) +var s_m: Mat; + +@group(0) @binding(1) +var u_m: Mat; + +fn access_m() { + var idx = 1; + idx--; + + // loads from storage + let l_s_m = s_m; + let l_s_c_c = s_m[0]; + let l_s_c_v = s_m[idx]; + let l_s_e_cc = s_m[0][0]; + let l_s_e_cv = s_m[0][idx]; + let l_s_e_vc = s_m[idx][0]; + let l_s_e_vv = s_m[idx][idx]; + + // loads from uniform + let l_u_m = u_m; + let l_u_c_c = u_m[0]; + let l_u_c_v = u_m[idx]; + let l_u_e_cc = u_m[0][0]; + let l_u_e_cv = u_m[0][idx]; + let l_u_e_vc = u_m[idx][0]; + let l_u_e_vv = u_m[idx][idx]; + + // stores to storage + s_m = l_u_m; + s_m[0] = l_u_c_c; + s_m[idx] = l_u_c_v; + s_m[0][0] = l_u_e_cc; + s_m[0][idx] = l_u_e_cv; + s_m[idx][0] = l_u_e_vc; + s_m[idx][idx] = l_u_e_vv; +} + +struct StructWithMat { + m: Mat, +} + +@group(1) @binding(0) +var s_sm: StructWithMat; + +@group(1) @binding(1) +var u_sm: StructWithMat; + +fn access_sm() { + var idx = 1; + idx--; + + // loads from storage + let l_s_s = s_sm; + let l_s_m = s_sm.m; + let l_s_c_c = s_sm.m[0]; + let l_s_c_v = s_sm.m[idx]; + let l_s_e_cc = s_sm.m[0][0]; + let l_s_e_cv = s_sm.m[0][idx]; + let l_s_e_vc = s_sm.m[idx][0]; + let l_s_e_vv = s_sm.m[idx][idx]; + + // loads from uniform + let l_u_s = u_sm; + let l_u_m = u_sm.m; + let l_u_c_c = u_sm.m[0]; + let l_u_c_v = u_sm.m[idx]; + let l_u_e_cc = u_sm.m[0][0]; + let l_u_e_cv = u_sm.m[0][idx]; + let l_u_e_vc = u_sm.m[idx][0]; + let l_u_e_vv = u_sm.m[idx][idx]; + + // stores to storage + s_sm = l_u_s; + s_sm.m = l_u_m; + s_sm.m[0] = l_u_c_c; + s_sm.m[idx] = l_u_c_v; + s_sm.m[0][0] = l_u_e_cc; + s_sm.m[0][idx] = l_u_e_cv; + s_sm.m[idx][0] = l_u_e_vc; + s_sm.m[idx][idx] = l_u_e_vv; +} + +struct StructWithArrayOfStructOfMat { + a: array, +} + +@group(2) @binding(0) +var s_sasm: StructWithArrayOfStructOfMat; + +@group(2) @binding(1) +var u_sasm: StructWithArrayOfStructOfMat; + +fn access_sasm() { + var idx = 1; + idx--; + + // loads from storage + let l_s_s = s_sasm; + let l_s_a = s_sasm.a; + let l_s_m_c = s_sasm.a[0].m; + let l_s_m_v = s_sasm.a[idx].m; + let l_s_c_cc = s_sasm.a[0].m[0]; + let l_s_c_cv = s_sasm.a[0].m[idx]; + let l_s_c_vc = s_sasm.a[idx].m[0]; + let l_s_c_vv = s_sasm.a[idx].m[idx]; + let l_s_e_ccc = s_sasm.a[0].m[0][0]; + let l_s_e_ccv = s_sasm.a[0].m[0][idx]; + let l_s_e_cvc = s_sasm.a[0].m[idx][0]; + let l_s_e_cvv = s_sasm.a[0].m[idx][idx]; + let l_s_e_vcc = s_sasm.a[idx].m[0][0]; + let l_s_e_vcv = s_sasm.a[idx].m[0][idx]; + let l_s_e_vvc = s_sasm.a[idx].m[idx][0]; + let l_s_e_vvv = s_sasm.a[idx].m[idx][idx]; + + // loads from uniform + let l_u_s = u_sasm; + let l_u_a = u_sasm.a; + let l_u_m_c = u_sasm.a[0].m; + let l_u_m_v = u_sasm.a[idx].m; + let l_u_c_cc = u_sasm.a[0].m[0]; + let l_u_c_cv = u_sasm.a[0].m[idx]; + let l_u_c_vc = u_sasm.a[idx].m[0]; + let l_u_c_vv = u_sasm.a[idx].m[idx]; + let l_u_e_ccc = u_sasm.a[0].m[0][0]; + let l_u_e_ccv = u_sasm.a[0].m[0][idx]; + let l_u_e_cvc = u_sasm.a[0].m[idx][0]; + let l_u_e_cvv = u_sasm.a[0].m[idx][idx]; + let l_u_e_vcc = u_sasm.a[idx].m[0][0]; + let l_u_e_vcv = u_sasm.a[idx].m[0][idx]; + let l_u_e_vvc = u_sasm.a[idx].m[idx][0]; + let l_u_e_vvv = u_sasm.a[idx].m[idx][idx]; + + // stores to storage + s_sasm = l_u_s; + s_sasm.a = l_u_a; + s_sasm.a[0].m = l_u_m_c; + s_sasm.a[idx].m = l_u_m_v; + s_sasm.a[0].m[0] = l_u_c_cc; + s_sasm.a[0].m[idx] = l_u_c_cv; + s_sasm.a[idx].m[0] = l_u_c_vc; + s_sasm.a[idx].m[idx] = l_u_c_vv; + s_sasm.a[0].m[0][0] = l_u_e_ccc; + s_sasm.a[0].m[0][idx] = l_u_e_ccv; + s_sasm.a[0].m[idx][0] = l_u_e_cvc; + s_sasm.a[0].m[idx][idx] = l_u_e_cvv; + s_sasm.a[idx].m[0][0] = l_u_e_vcc; + s_sasm.a[idx].m[0][idx] = l_u_e_vcv; + s_sasm.a[idx].m[idx][0] = l_u_e_vvc; + s_sasm.a[idx].m[idx][idx] = l_u_e_vvv; +} + +@compute @workgroup_size(1) +fn main() { + access_m(); + access_sm(); + access_sasm(); +} diff --git a/naga/tests/in/wgsl/math-functions.toml b/naga/tests/in/wgsl/math-functions.toml new file mode 100644 index 00000000000..96a1d507b4f --- /dev/null +++ b/naga/tests/in/wgsl/math-functions.toml @@ -0,0 +1,2 @@ +# TODO(https://github.com/gfx-rs/wgpu/issues/8154): enable only `F16_IN_F32` capability +god_mode = true diff --git a/naga/tests/in/wgsl/msl-vpt-formats-x1.toml b/naga/tests/in/wgsl/msl-vpt-formats-x1.toml index 0e8247ca9ac..50af398181b 100644 --- a/naga/tests/in/wgsl/msl-vpt-formats-x1.toml +++ b/naga/tests/in/wgsl/msl-vpt-formats-x1.toml @@ -49,5 +49,5 @@ attributes = [ { offset = 640, shader_location = 40, format = "unorm8x4-bgra" }, ] id = 1 -indexed_by_vertex = true +step_mode = "ByVertex" stride = 644 diff --git a/naga/tests/in/wgsl/msl-vpt-formats-x2.toml b/naga/tests/in/wgsl/msl-vpt-formats-x2.toml index 0e8247ca9ac..50af398181b 100644 --- a/naga/tests/in/wgsl/msl-vpt-formats-x2.toml +++ b/naga/tests/in/wgsl/msl-vpt-formats-x2.toml @@ -49,5 +49,5 @@ attributes = [ { offset = 640, shader_location = 40, format = "unorm8x4-bgra" }, ] id = 1 -indexed_by_vertex = true +step_mode = "ByVertex" stride = 644 diff --git a/naga/tests/in/wgsl/msl-vpt-formats-x3.toml b/naga/tests/in/wgsl/msl-vpt-formats-x3.toml index 0e8247ca9ac..50af398181b 100644 --- a/naga/tests/in/wgsl/msl-vpt-formats-x3.toml +++ b/naga/tests/in/wgsl/msl-vpt-formats-x3.toml @@ -49,5 +49,5 @@ attributes = [ { offset = 640, shader_location = 40, format = "unorm8x4-bgra" }, ] id = 1 -indexed_by_vertex = true +step_mode = "ByVertex" stride = 644 diff --git a/naga/tests/in/wgsl/msl-vpt-formats-x4.toml b/naga/tests/in/wgsl/msl-vpt-formats-x4.toml index 0e8247ca9ac..50af398181b 100644 --- a/naga/tests/in/wgsl/msl-vpt-formats-x4.toml +++ b/naga/tests/in/wgsl/msl-vpt-formats-x4.toml @@ -49,5 +49,5 @@ attributes = [ { offset = 640, shader_location = 40, format = "unorm8x4-bgra" }, ] id = 1 -indexed_by_vertex = true +step_mode = "ByVertex" stride = 644 diff --git a/naga/tests/in/wgsl/msl-vpt.toml b/naga/tests/in/wgsl/msl-vpt.toml index d6cc7bfd2f5..ee6b0b52a53 100644 --- a/naga/tests/in/wgsl/msl-vpt.toml +++ b/naga/tests/in/wgsl/msl-vpt.toml @@ -10,11 +10,11 @@ attributes = [ { offset = 4, shader_location = 1, format = "Float32x4" }, ] id = 1 -indexed_by_vertex = true +step_mode = "ByVertex" stride = 20 [[msl_pipeline.vertex_buffer_mappings]] attributes = [{ offset = 0, shader_location = 2, format = "Float32x2" }] id = 2 -indexed_by_vertex = false +step_mode = "ByInstance" stride = 16 diff --git a/naga/tests/in/wgsl/texture-external.toml b/naga/tests/in/wgsl/texture-external.toml new file mode 100644 index 00000000000..3a44f0265ab --- /dev/null +++ b/naga/tests/in/wgsl/texture-external.toml @@ -0,0 +1,45 @@ +god_mode = true +targets = "HLSL | IR | METAL | WGSL" + +[msl.per_entry_point_map.compute_main] +resources = [ + { bind_target = { external_texture = { planes = [ + 0, + 1, + 2, + ], params = 0 } }, resource_binding = { group = 0, binding = 0 } }, + { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 1 } }, +] + +[msl.per_entry_point_map.fragment_main] +resources = [ + { bind_target = { external_texture = { planes = [ + 0, + 1, + 2, + ], params = 0 } }, resource_binding = { group = 0, binding = 0 } }, + { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 1 } }, +] + +[msl.per_entry_point_map.vertex_main] +resources = [ + { bind_target = { external_texture = { planes = [ + 0, + 1, + 2, + ], params = 0 } }, resource_binding = { group = 0, binding = 0 } }, + { bind_target = { sampler.Resource = 0 }, resource_binding = { group = 0, binding = 1 } }, +] + +[[hlsl.binding_map]] +resource_binding = { group = 0, binding = 1 } +bind_target = { register = 0, space = 0 } + +[[hlsl.external_texture_binding_map]] +resource_binding = { group = 0, binding = 0 } +bind_target.planes = [ + { space = 0, register = 0 }, + { space = 0, register = 1 }, + { space = 0, register = 2 }, +] +bind_target.params = { space = 0, register = 3 } diff --git a/naga/tests/in/wgsl/texture-external.wgsl b/naga/tests/in/wgsl/texture-external.wgsl new file mode 100644 index 00000000000..3bc0a44acb6 --- /dev/null +++ b/naga/tests/in/wgsl/texture-external.wgsl @@ -0,0 +1,28 @@ +@group(0) @binding(0) +var tex: texture_external; +@group(0) @binding(1) +var samp: sampler; + +fn test(t: texture_external) -> vec4 { + var a = textureSampleBaseClampToEdge(t, samp, vec2(0.0f)); + var b = textureLoad(t, vec2(0i)); + var c = textureLoad(t, vec2(0u)); + var d = textureDimensions(t); + + return a + b + c + vec2f(d).xyxy; +} + +@fragment +fn fragment_main() -> @location(0) vec4 { + return test(tex); +} + +@vertex +fn vertex_main() -> @builtin(position) vec4 { + return test(tex); +} + +@compute @workgroup_size(1) +fn compute_main() { + test(tex); +} diff --git a/naga/tests/naga/example_wgsl.rs b/naga/tests/naga/example_wgsl.rs index 5edb39af837..2e1062d7b67 100644 --- a/naga/tests/naga/example_wgsl.rs +++ b/naga/tests/naga/example_wgsl.rs @@ -4,6 +4,9 @@ use naga::{front::wgsl, valid::Validator}; use std::{ffi::OsStr, fs, path::Path}; /// Runs through all example shaders and ensures they are valid wgsl. +// While we _can_ run this test under miri, it is extremely slow (>5 minutes), +// and naga isn't the primary target for miri testing, so we disable it. +#[cfg_attr(miri, ignore)] #[test] pub fn parse_example_wgsl() { let example_path = Path::new(env!("CARGO_MANIFEST_DIR")) diff --git a/naga/tests/naga/snapshots.rs b/naga/tests/naga/snapshots.rs index 32e2f5e0285..e01610f29a8 100644 --- a/naga/tests/naga/snapshots.rs +++ b/naga/tests/naga/snapshots.rs @@ -1,390 +1,13 @@ -// A lot of the code can be unused based on configuration flags, -// the corresponding warnings aren't helpful. -#![allow(dead_code, unused_imports)] - -use core::fmt::Write; - -use std::{ - fs, - path::{Path, PathBuf}, -}; - use naga::compact::KeepUnused; -use ron::de; - -const CRATE_ROOT: &str = env!("CARGO_MANIFEST_DIR"); -const BASE_DIR_IN: &str = "tests/in"; -const BASE_DIR_OUT: &str = "tests/out"; - -bitflags::bitflags! { - #[derive(Clone, Copy, serde::Deserialize)] - #[serde(transparent)] - #[derive(Debug, Eq, PartialEq)] - struct Targets: u32 { - /// A serialization of the `naga::Module`, in RON format. - const IR = 1; - - /// A serialization of the `naga::valid::ModuleInfo`, in RON format. - const ANALYSIS = 1 << 1; - - const SPIRV = 1 << 2; - const METAL = 1 << 3; - const GLSL = 1 << 4; - const DOT = 1 << 5; - const HLSL = 1 << 6; - const WGSL = 1 << 7; - const NO_VALIDATION = 1 << 8; - } -} - -impl Targets { - /// Defaults for `spv` and `glsl` snapshots. - fn non_wgsl_default() -> Self { - Targets::WGSL - } - - /// Defaults for `wgsl` snapshots. - fn wgsl_default() -> Self { - Targets::HLSL | Targets::SPIRV | Targets::GLSL | Targets::METAL | Targets::WGSL - } -} - -#[derive(serde::Deserialize)] -struct SpvOutVersion(u8, u8); -impl Default for SpvOutVersion { - fn default() -> Self { - SpvOutVersion(1, 1) - } -} - -#[cfg(all(feature = "deserialize", spv_out))] -#[derive(serde::Deserialize)] -struct BindingMapSerialization { - resource_binding: naga::ResourceBinding, - bind_target: naga::back::spv::BindingInfo, -} - -#[cfg(all(feature = "deserialize", spv_out))] -fn deserialize_binding_map<'de, D>(deserializer: D) -> Result -where - D: serde::Deserializer<'de>, -{ - use serde::Deserialize; - - let vec = Vec::::deserialize(deserializer)?; - let mut map = naga::back::spv::BindingMap::default(); - for item in vec { - map.insert(item.resource_binding, item.bind_target); - } - Ok(map) -} - -#[derive(Default, serde::Deserialize)] -#[serde(default)] -struct WgslInParameters { - parse_doc_comments: bool, -} - -#[derive(Default, serde::Deserialize)] -#[serde(default)] -struct SpirvInParameters { - adjust_coordinate_space: bool, -} - -#[derive(Default, serde::Deserialize)] -#[serde(default)] -struct SpirvOutParameters { - version: SpvOutVersion, - capabilities: naga::FastHashSet, - debug: bool, - adjust_coordinate_space: bool, - force_point_size: bool, - clamp_frag_depth: bool, - separate_entry_points: bool, - #[cfg(all(feature = "deserialize", spv_out))] - #[serde(deserialize_with = "deserialize_binding_map")] - binding_map: naga::back::spv::BindingMap, -} - -#[derive(Default, serde::Deserialize)] -#[serde(default)] -struct WgslOutParameters { - explicit_types: bool, -} - -#[derive(Default, serde::Deserialize)] -struct FragmentModule { - path: String, - entry_point: String, -} - -#[derive(Default, serde::Deserialize)] -#[serde(default)] -struct Parameters { - // -- GOD MODE -- - god_mode: bool, - - // -- wgsl-in options -- - #[serde(rename = "wgsl-in")] - wgsl_in: WgslInParameters, - - // -- spirv-in options -- - #[serde(rename = "spv-in")] - spv_in: SpirvInParameters, - - // -- SPIR-V options -- - spv: SpirvOutParameters, - - /// Defaults to [`Targets::non_wgsl_default()`] for `spv` and `glsl` snapshots, - /// and [`Targets::wgsl_default()`] for `wgsl` snapshots. - targets: Option, - - // -- MSL options -- - #[cfg(all(feature = "deserialize", msl_out))] - msl: naga::back::msl::Options, - #[cfg(all(feature = "deserialize", msl_out))] - #[serde(default)] - msl_pipeline: naga::back::msl::PipelineOptions, - - // -- GLSL options -- - #[cfg(all(feature = "deserialize", glsl_out))] - glsl: naga::back::glsl::Options, - glsl_exclude_list: naga::FastHashSet, - #[cfg(all(feature = "deserialize", glsl_out))] - glsl_multiview: Option, - - // -- HLSL options -- - #[cfg(all(feature = "deserialize", hlsl_out))] - hlsl: naga::back::hlsl::Options, - - // -- WGSL options -- - wgsl: WgslOutParameters, - - // -- General options -- - - // Allow backends to be aware of the fragment module. - // Is the name of a WGSL file in the same directory as the test file. - fragment_module: Option, - - #[cfg(feature = "deserialize")] - bounds_check_policies: naga::proc::BoundsCheckPolicies, - - #[cfg(all(feature = "deserialize", any(hlsl_out, msl_out, spv_out, glsl_out)))] - pipeline_constants: naga::back::PipelineConstants, -} - -/// Information about a shader input file. -#[derive(Debug)] -struct Input { - /// The subdirectory of `tests/in` to which this input belongs, if any. - /// - /// If the subdirectory is omitted, we assume that the output goes - /// to "wgsl". - subdirectory: PathBuf, - - /// The input filename name, without a directory. - file_name: PathBuf, - - /// True if output filenames should add the output extension on top of - /// `file_name`'s existing extension, rather than replacing it. - /// - /// This is used by `convert_snapshots_glsl`, which wants to take input files - /// like `210-bevy-2d-shader.frag` and just add `.wgsl` to it, producing - /// `210-bevy-2d-shader.frag.wgsl`. - keep_input_extension: bool, -} - -impl Input { - /// Read an input file and its corresponding parameters file. - /// - /// Given `input`, the relative path of a shader input file, return - /// a `Source` value containing its path, code, and parameters. - /// - /// The `input` path is interpreted relative to the `BASE_DIR_IN` - /// subdirectory of the directory given by the `CARGO_MANIFEST_DIR` - /// environment variable. - fn new(subdirectory: &str, name: &str, extension: &str) -> Input { - Input { - subdirectory: PathBuf::from(subdirectory), - // Don't wipe out any extensions on `name`, as - // `with_extension` would do. - file_name: PathBuf::from(format!("{name}.{extension}")), - keep_input_extension: false, - } - } - - /// Return an iterator that produces an `Input` for each entry in `subdirectory`. - fn files_in_dir( - subdirectory: &'static str, - file_extensions: &'static [&'static str], - ) -> impl Iterator + 'static { - let input_directory = Path::new(CRATE_ROOT).join(BASE_DIR_IN).join(subdirectory); - - let entries = match std::fs::read_dir(&input_directory) { - Ok(entries) => entries, - Err(err) => panic!( - "Error opening directory '{}': {}", - input_directory.display(), - err - ), - }; - - entries.filter_map(move |result| { - let entry = result.expect("error reading directory"); - if !entry.file_type().unwrap().is_file() { - return None; - } - - let file_name = PathBuf::from(entry.file_name()); - let extension = file_name - .extension() - .expect("all files in snapshot input directory should have extensions"); - - if !file_extensions.contains(&extension.to_str().unwrap()) { - return None; - } - - if let Ok(pat) = std::env::var("NAGA_SNAPSHOT") { - if !file_name.to_string_lossy().contains(&pat) { - return None; - } - } - - let input = Input::new( - subdirectory, - file_name.file_stem().unwrap().to_str().unwrap(), - extension.to_str().unwrap(), - ); - Some(input) - }) - } - - /// Return the path to the input directory. - fn input_directory(&self) -> PathBuf { - let mut dir = Path::new(CRATE_ROOT).join(BASE_DIR_IN); - dir.push(&self.subdirectory); - dir - } - - /// Return the path to the output directory. - fn output_directory(subdirectory: &str) -> PathBuf { - let mut dir = Path::new(CRATE_ROOT).join(BASE_DIR_OUT); - dir.push(subdirectory); - dir - } - - /// Return the path to the input file. - fn input_path(&self) -> PathBuf { - let mut input = self.input_directory(); - input.push(&self.file_name); - input - } - - fn output_path(&self, subdirectory: &str, extension: &str) -> PathBuf { - let mut output = Self::output_directory(subdirectory); - if self.keep_input_extension { - let file_name = format!( - "{}-{}.{}", - self.subdirectory.display(), - self.file_name.display(), - extension - ); - - output.push(&file_name); - } else { - let file_name = format!( - "{}-{}", - self.subdirectory.display(), - self.file_name.display() - ); - - output.push(&file_name); - output.set_extension(extension); - } - output - } - - /// Return the contents of the input file as a string. - fn read_source(&self) -> String { - println!("Processing '{}'", self.file_name.display()); - let input_path = self.input_path(); - match fs::read_to_string(&input_path) { - Ok(source) => source, - Err(err) => { - panic!( - "Couldn't read shader input file `{}`: {}", - input_path.display(), - err - ); - } - } - } - - /// Return the contents of the input file as a vector of bytes. - fn read_bytes(&self) -> Vec { - println!("Processing '{}'", self.file_name.display()); - let input_path = self.input_path(); - match fs::read(&input_path) { - Ok(bytes) => bytes, - Err(err) => { - panic!( - "Couldn't read shader input file `{}`: {}", - input_path.display(), - err - ); - } - } - } - - /// Return this input's parameter file, parsed. - fn read_parameters(&self) -> Parameters { - let mut param_path = self.input_path(); - param_path.set_extension("toml"); - let mut params = match fs::read_to_string(¶m_path) { - Ok(string) => match toml::de::from_str(&string) { - Ok(params) => params, - Err(e) => panic!( - "Couldn't parse param file: {} due to: {e}", - param_path.display() - ), - }, - Err(_) => Parameters::default(), - }; - - if params.targets.is_none() { - match self.input_path().extension().unwrap().to_str().unwrap() { - "wgsl" => params.targets = Some(Targets::wgsl_default()), - "spvasm" => params.targets = Some(Targets::non_wgsl_default()), - "vert" | "frag" | "comp" => params.targets = Some(Targets::non_wgsl_default()), - e => { - panic!("Unknown extension: {}", e); - } - } - } - - params - } - - /// Write `data` to a file corresponding to this input file in - /// `subdirectory`, with `extension`. - fn write_output_file(&self, subdirectory: &str, extension: &str, data: impl AsRef<[u8]>) { - let output_path = self.output_path(subdirectory, extension); - fs::create_dir_all(output_path.parent().unwrap()).unwrap(); - if let Err(err) = fs::write(&output_path, data) { - panic!("Error writing {}: {}", output_path.display(), err); - } - } -} +use naga_test::*; -#[cfg(hlsl_out)] -type FragmentEntryPoint<'a> = naga::back::hlsl::FragmentEntryPoint<'a>; -#[cfg(not(hlsl_out))] -type FragmentEntryPoint<'a> = (); +const DIR_IN: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/in"); +const DIR_OUT: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/out"); #[allow(unused_variables)] fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<&str>) { - let params = input.read_parameters(); - let name = &input.file_name; + let params = input.read_parameters(DIR_IN); + let name = input.file_name.display().to_string(); let targets = params.targets.unwrap(); @@ -402,12 +25,11 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<& ) }; - #[cfg(feature = "serialize")] { if targets.contains(Targets::IR) { let config = ron::ser::PrettyConfig::default().new_line("\n".to_string()); let string = ron::ser::to_string_pretty(module, config).unwrap(); - input.write_output_file("ir", "ron", string); + input.write_output_file("ir", "ron", string, DIR_OUT); } } @@ -422,11 +44,7 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<& .subgroup_operations(subgroup_operations) .validate(module) .unwrap_or_else(|err| { - panic!( - "Naga module validation failed on test `{}`:\n{:?}", - name.display(), - err - ); + panic!("Naga module validation failed on test `{name}`:\n{err:?}"); }); let info = { @@ -438,12 +56,11 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<& // snapshots makes the output independent of unused arena entries. naga::compact::compact(module, KeepUnused::No); - #[cfg(feature = "serialize")] { if targets.contains(Targets::IR) { let config = ron::ser::PrettyConfig::default().new_line("\n".to_string()); let string = ron::ser::to_string_pretty(module, config).unwrap(); - input.write_output_file("ir", "compact.ron", string); + input.write_output_file("ir", "compact.ron", string, DIR_OUT); } } @@ -452,138 +69,116 @@ fn check_targets(input: &Input, module: &mut naga::Module, source_code: Option<& .subgroup_operations(subgroup_operations) .validate(module) .unwrap_or_else(|err| { - panic!( - "Post-compaction module validation failed on test '{}':\n<{:?}", - name.display(), - err, - ) + panic!("Post-compaction module validation failed on test '{name}':\n<{err:?}") }) }; - #[cfg(feature = "serialize")] { if targets.contains(Targets::ANALYSIS) { let config = ron::ser::PrettyConfig::default().new_line("\n".to_string()); let string = ron::ser::to_string_pretty(&info, config).unwrap(); - input.write_output_file("analysis", "info.ron", string); + input.write_output_file("analysis", "info.ron", string, DIR_OUT); } } - #[cfg(all(feature = "deserialize", spv_out))] - { - if targets.contains(Targets::SPIRV) { - let mut debug_info = None; - if let Some(source_code) = source_code { - debug_info = Some(naga::back::spv::DebugInfo { - source_code, - file_name: name.as_path().into(), - // wgpu#6266: we technically know all the information here to - // produce the valid language but it's not too important for - // validation purposes - language: naga::back::spv::SourceLanguage::Unknown, - }) - } - - write_output_spv( - input, - module, - &info, - debug_info, - ¶ms.spv, - params.bounds_check_policies, - ¶ms.pipeline_constants, - ); + if targets.contains(Targets::SPIRV) { + let mut debug_info = None; + if let Some(source_code) = source_code { + debug_info = Some(naga::back::spv::DebugInfo { + source_code, + file_name: &name, + // wgpu#6266: we technically know all the information here to + // produce the valid language but it's not too important for + // validation purposes + language: naga::back::spv::SourceLanguage::Unknown, + }) } + + write_output_spv( + input, + module, + &info, + debug_info, + ¶ms.spv, + params.bounds_check_policies, + ¶ms.pipeline_constants, + ); } - #[cfg(all(feature = "deserialize", msl_out))] - { - if targets.contains(Targets::METAL) { - write_output_msl( + + if targets.contains(Targets::METAL) { + write_output_msl( + input, + module, + &info, + ¶ms.msl, + ¶ms.msl_pipeline, + params.bounds_check_policies, + ¶ms.pipeline_constants, + ); + } + + if targets.contains(Targets::GLSL) { + for ep in module.entry_points.iter() { + if params.glsl_exclude_list.contains(&ep.name) { + continue; + } + write_output_glsl( input, module, &info, - ¶ms.msl, - ¶ms.msl_pipeline, + ep.stage, + &ep.name, + ¶ms.glsl, params.bounds_check_policies, + params.glsl_multiview, ¶ms.pipeline_constants, ); } } - #[cfg(all(feature = "deserialize", glsl_out))] - { - if targets.contains(Targets::GLSL) { - for ep in module.entry_points.iter() { - if params.glsl_exclude_list.contains(&ep.name) { - continue; - } - write_output_glsl( - input, - module, - &info, - ep.stage, - &ep.name, - ¶ms.glsl, - params.bounds_check_policies, - params.glsl_multiview, - ¶ms.pipeline_constants, - ); - } - } - } - #[cfg(dot_out)] - { - if targets.contains(Targets::DOT) { - let string = naga::back::dot::write(module, Some(&info), Default::default()).unwrap(); - input.write_output_file("dot", "dot", string); - } + + if targets.contains(Targets::DOT) { + let string = naga::back::dot::write(module, Some(&info), Default::default()).unwrap(); + input.write_output_file("dot", "dot", string, DIR_OUT); } - #[cfg(all(feature = "deserialize", hlsl_out))] - { - if targets.contains(Targets::HLSL) { - let frag_module; - let mut frag_ep = None; - if let Some(ref module_spec) = params.fragment_module { - let full_path = input.input_directory().join(&module_spec.path); - - assert_eq!( - full_path.extension().unwrap().to_string_lossy(), - "wgsl", - "Currently all fragment modules must be in WGSL" - ); - - let frag_src = fs::read_to_string(full_path).unwrap(); - - frag_module = naga::front::wgsl::parse_str(&frag_src) - .expect("Failed to parse fragment module"); - - frag_ep = Some( - naga::back::hlsl::FragmentEntryPoint::new( - &frag_module, - &module_spec.entry_point, - ) - .expect("Could not find fragment entry point"), - ); - } - write_output_hlsl( - input, - module, - &info, - ¶ms.hlsl, - ¶ms.pipeline_constants, - frag_ep, + if targets.contains(Targets::HLSL) { + let frag_module; + let mut frag_ep = None; + if let Some(ref module_spec) = params.fragment_module { + let full_path = input.input_directory(DIR_IN).join(&module_spec.path); + + assert_eq!( + full_path.extension().unwrap().to_string_lossy(), + "wgsl", + "Currently all fragment modules must be in WGSL" + ); + + let frag_src = std::fs::read_to_string(full_path).unwrap(); + + frag_module = + naga::front::wgsl::parse_str(&frag_src).expect("Failed to parse fragment module"); + + frag_ep = Some( + naga::back::hlsl::FragmentEntryPoint::new(&frag_module, &module_spec.entry_point) + .expect("Could not find fragment entry point"), ); } + + write_output_hlsl( + input, + module, + &info, + ¶ms.hlsl, + ¶ms.pipeline_constants, + frag_ep, + ); } - #[cfg(all(feature = "deserialize", wgsl_out))] - { - if targets.contains(Targets::WGSL) { - write_output_wgsl(input, module, &info, ¶ms.wgsl); - } + + if targets.contains(Targets::WGSL) { + write_output_wgsl(input, module, &info, ¶ms.wgsl); } } -#[cfg(spv_out)] fn write_output_spv( input: &Input, module: &naga::Module, @@ -594,31 +189,8 @@ fn write_output_spv( pipeline_constants: &naga::back::PipelineConstants, ) { use naga::back::spv; - use rspirv::binary::Disassemble; - let mut flags = spv::WriterFlags::LABEL_VARYINGS; - flags.set(spv::WriterFlags::DEBUG, params.debug); - flags.set( - spv::WriterFlags::ADJUST_COORDINATE_SPACE, - params.adjust_coordinate_space, - ); - flags.set(spv::WriterFlags::FORCE_POINT_SIZE, params.force_point_size); - flags.set(spv::WriterFlags::CLAMP_FRAG_DEPTH, params.clamp_frag_depth); - - let options = spv::Options { - lang_version: (params.version.0, params.version.1), - flags, - capabilities: if params.capabilities.is_empty() { - None - } else { - Some(params.capabilities.clone()) - }, - bounds_check_policies, - binding_map: params.binding_map.clone(), - zero_initialize_workgroup_memory: spv::ZeroInitializeWorkgroupMemoryMode::Polyfill, - force_loop_bounding: true, - debug_info, - }; + let options = params.to_options(bounds_check_policies, debug_info); let (module, info) = naga::back::pipeline_constants::process_overrides(module, info, None, pipeline_constants) @@ -644,7 +216,6 @@ fn write_output_spv( } } -#[cfg(spv_out)] fn write_output_spv_inner( input: &Input, module: &naga::Module, @@ -667,10 +238,9 @@ fn write_output_spv_inner( } else { dis }; - input.write_output_file("spv", extension, dis); + input.write_output_file("spv", extension, dis, DIR_OUT); } -#[cfg(msl_out)] fn write_output_msl( input: &Input, module: &naga::Module, @@ -699,10 +269,9 @@ fn write_output_msl( } } - input.write_output_file("msl", "msl", string); + input.write_output_file("msl", "msl", string, DIR_OUT); } -#[cfg(glsl_out)] #[allow(clippy::too_many_arguments)] fn write_output_glsl( input: &Input, @@ -741,10 +310,9 @@ fn write_output_glsl( writer.write().expect("GLSL write failed"); let extension = format!("{ep_name}.{stage:?}.glsl"); - input.write_output_file("glsl", &extension, buffer); + input.write_output_file("glsl", &extension, buffer, DIR_OUT); } -#[cfg(hlsl_out)] fn write_output_hlsl( input: &Input, module: &naga::Module, @@ -753,7 +321,6 @@ fn write_output_hlsl( pipeline_constants: &naga::back::PipelineConstants, frag_ep: Option, ) { - use core::fmt::Write as _; use naga::back::hlsl; println!("generating HLSL"); @@ -769,7 +336,7 @@ fn write_output_hlsl( .write(&module, &info, frag_ep.as_ref()) .expect("HLSL write failed"); - input.write_output_file("hlsl", "hlsl", buffer); + input.write_output_file("hlsl", "hlsl", buffer, DIR_OUT); // We need a config file for validation script // This file contains an info about profiles (shader stages) contains inside generated shader @@ -796,10 +363,11 @@ fn write_output_hlsl( }); } - config.to_file(input.output_path("hlsl", "ron")).unwrap(); + config + .to_file(input.output_path("hlsl", "ron", DIR_OUT)) + .unwrap(); } -#[cfg(wgsl_out)] fn write_output_wgsl( input: &Input, module: &naga::Module, @@ -810,51 +378,49 @@ fn write_output_wgsl( println!("generating WGSL"); - let mut flags = wgsl::WriterFlags::empty(); - flags.set(wgsl::WriterFlags::EXPLICIT_TYPES, params.explicit_types); + let string = wgsl::write_string(module, info, params.into()).expect("WGSL write failed"); - let string = wgsl::write_string(module, info, flags).expect("WGSL write failed"); - - input.write_output_file("wgsl", "wgsl", string); + input.write_output_file("wgsl", "wgsl", string, DIR_OUT); } -#[cfg(feature = "wgsl-in")] +// While we _can_ run this test under miri, it is extremely slow (>5 minutes), +// and naga isn't the primary target for miri testing, so we disable it. +#[cfg_attr(miri, ignore)] #[test] fn convert_snapshots_wgsl() { let _ = env_logger::try_init(); - for input in Input::files_in_dir("wgsl", &["wgsl"]) { - let source = input.read_source(); + for input in Input::files_in_dir("wgsl", &["wgsl"], DIR_IN) { + let source = input.read_source(DIR_IN, true); // crlf will make the large split output different on different platform let source = source.replace('\r', ""); - let params = input.read_parameters(); - let WgslInParameters { parse_doc_comments } = params.wgsl_in; + let params = input.read_parameters(DIR_IN); - let options = naga::front::wgsl::Options { parse_doc_comments }; - let mut frontend = naga::front::wgsl::Frontend::new_with_options(options); + let mut frontend = naga::front::wgsl::Frontend::new_with_options((¶ms.wgsl_in).into()); match frontend.parse(&source) { Ok(mut module) => check_targets(&input, &mut module, Some(&source)), Err(e) => panic!( "{}", - e.emit_to_string_with_path(&source, input.input_path()) + e.emit_to_string_with_path(&source, input.input_path(DIR_IN)) ), } } } -#[cfg(feature = "spv-in")] +// miri doesn't allow us to shell out to `spirv-as` +#[cfg_attr(miri, ignore)] #[test] fn convert_snapshots_spv() { use std::process::Command; let _ = env_logger::try_init(); - for input in Input::files_in_dir("spv", &["spvasm"]) { + for input in Input::files_in_dir("spv", &["spvasm"], DIR_IN) { println!("Assembling '{}'", input.file_name.display()); let command = Command::new("spirv-as") - .arg(input.input_path()) + .arg(input.input_path(DIR_IN)) .arg("-o") .arg("-") .output() @@ -873,32 +439,24 @@ fn convert_snapshots_spv() { ); } - let params = input.read_parameters(); - let SpirvInParameters { - adjust_coordinate_space, - } = params.spv_in; - - let mut module = naga::front::spv::parse_u8_slice( - &command.stdout, - &naga::front::spv::Options { - adjust_coordinate_space, - strict_capabilities: true, - ..Default::default() - }, - ) - .unwrap(); + let params = input.read_parameters(DIR_IN); + + let mut module = + naga::front::spv::parse_u8_slice(&command.stdout, &(¶ms.spv_in).into()).unwrap(); check_targets(&input, &mut module, None); } } -#[cfg(feature = "glsl-in")] +// While we _can_ run this test under miri, it is extremely slow (>5 minutes), +// and naga isn't the primary target for miri testing, so we disable it. +#[cfg_attr(miri, ignore)] #[allow(unused_variables)] #[test] fn convert_snapshots_glsl() { let _ = env_logger::try_init(); - for input in Input::files_in_dir("glsl", &["vert", "frag", "comp"]) { + for input in Input::files_in_dir("glsl", &["vert", "frag", "comp"], DIR_IN) { let input = Input { keep_input_extension: true, ..input @@ -919,7 +477,7 @@ fn convert_snapshots_glsl() { stage, defines: Default::default(), }, - &input.read_source(), + &input.read_source(DIR_IN, true), ) .unwrap(); diff --git a/naga/tests/naga/spirv_capabilities.rs b/naga/tests/naga/spirv_capabilities.rs index 2d46e37f72d..3ac0efa2407 100644 --- a/naga/tests/naga/spirv_capabilities.rs +++ b/naga/tests/naga/spirv_capabilities.rs @@ -6,6 +6,8 @@ Test SPIR-V backend capability checks. use spirv::Capability as Ca; +use rspirv::binary::Disassemble; + fn capabilities_used(source: &str) -> naga::FastIndexSet { use naga::back::spv; use naga::valid; @@ -213,3 +215,134 @@ fn int64() { fn float16() { require(&[Ca::Float16], "enable f16; fn f(x: f16) { }"); } + +#[test] +fn f16_io_capabilities() { + let source = r#" + enable f16; + + struct VertexOutput { + @location(0) color: vec3, + } + + @fragment + fn main(input: VertexOutput) -> @location(0) vec4 { + return vec4(input.color, f16(1.0)); + } + "#; + + use naga::back::spv; + use naga::valid; + + let module = naga::front::wgsl::parse_str(source).unwrap(); + let info = valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all()) + .validate(&module) + .unwrap(); + + // Test native path: use_storage_input_output_16 = true + let options_native = spv::Options { + use_storage_input_output_16: true, + ..Default::default() + }; + + let mut words_native = vec![]; + let mut writer_native = spv::Writer::new(&options_native).unwrap(); + writer_native + .write(&module, &info, None, &None, &mut words_native) + .unwrap(); + let caps_native = writer_native.get_capabilities_used(); + + // Should include `StorageInputOutput16` for native `f16` I/O + assert!(caps_native.contains(&Ca::StorageInputOutput16)); + + // Test polyfill path: use_storage_input_output_16 = false + let options_polyfill = spv::Options { + use_storage_input_output_16: false, + ..Default::default() + }; + + let mut words_polyfill = vec![]; + let mut writer_polyfill = spv::Writer::new(&options_polyfill).unwrap(); + writer_polyfill + .write(&module, &info, None, &None, &mut words_polyfill) + .unwrap(); + let caps_polyfill = writer_polyfill.get_capabilities_used(); + + // Should not include `StorageInputOutput16` when polyfilled + assert!(!caps_polyfill.contains(&Ca::StorageInputOutput16)); + + // But should still include the basic `f16` capabilities + assert!(caps_polyfill.contains(&Ca::Float16)); +} + +#[test] +fn f16_io_polyfill_codegen() { + let source = r#" + enable f16; + + struct F16IO { + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + } + + @fragment + fn main(input: F16IO) -> F16IO { + var output = input; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.vec2_f16.x = input.vec2_f16.y; + return output; + } + "#; + + use naga::{back::spv, valid}; + + let module = naga::front::wgsl::parse_str(source).unwrap(); + let info = valid::Validator::new(valid::ValidationFlags::all(), valid::Capabilities::all()) + .validate(&module) + .unwrap(); + + // Test Native Path + let options_native = spv::Options { + use_storage_input_output_16: true, + ..Default::default() + }; + let mut words_native = vec![]; + let mut writer_native = spv::Writer::new(&options_native).unwrap(); + writer_native + .write(&module, &info, None, &None, &mut words_native) + .unwrap(); + let caps_native = writer_native.get_capabilities_used(); + let dis_native = rspirv::dr::load_words(words_native).unwrap().disassemble(); + + // Native path must request the capability and must NOT have conversions. + assert!(caps_native.contains(&Ca::StorageInputOutput16)); + assert!(!dis_native.contains("OpFConvert")); + + // Test Polyfill Path + let options_polyfill = spv::Options { + use_storage_input_output_16: false, + ..Default::default() + }; + let mut words_polyfill = vec![]; + let mut writer_polyfill = spv::Writer::new(&options_polyfill).unwrap(); + writer_polyfill + .write(&module, &info, None, &None, &mut words_polyfill) + .unwrap(); + let caps_polyfill = writer_polyfill.get_capabilities_used(); + let dis_polyfill = rspirv::dr::load_words(words_polyfill) + .unwrap() + .disassemble(); + + // Polyfill path should request the capability but not have conversions. + assert!(!caps_polyfill.contains(&Ca::StorageInputOutput16)); + assert!(dis_polyfill.contains("OpFConvert")); + + // Should have 2 input conversions, and 2 output conversions + let fconvert_count = dis_polyfill.matches("OpFConvert").count(); + assert_eq!( + fconvert_count, 4, + "Expected 4 OpFConvert instructions for polyfilled I/O" + ); +} diff --git a/naga/tests/naga/validation.rs b/naga/tests/naga/validation.rs index b7ba70ee028..7c12e79620f 100644 --- a/naga/tests/naga/validation.rs +++ b/naga/tests/naga/validation.rs @@ -1,3 +1,12 @@ +//! Tests of the module validator. +//! +//! There are also some validation tests in [`wgsl_errors`](super::wgsl_errors). + +#![allow( + // We need to investigate these. + clippy::result_large_err +)] + use naga::{ ir, valid::{self, ModuleInfo}, @@ -29,7 +38,7 @@ fn populate_atomic_result() { // the differences between the test cases. fn try_variant( variant: Variant, - ) -> Result> { + ) -> Result> { let span = naga::Span::default(); let mut module = Module::default(); let ty_u32 = module.types.insert( @@ -130,7 +139,7 @@ fn populate_call_result() { // the differences between the test cases. fn try_variant( variant: Variant, - ) -> Result> { + ) -> Result> { let span = naga::Span::default(); let mut module = Module::default(); let ty_u32 = module.types.insert( @@ -206,9 +215,7 @@ fn emit_workgroup_uniform_load_result() { // // Looking at uses of the `wg_load` makes it easy to identify the // differences between the two variants. - fn variant( - wg_load: bool, - ) -> Result> { + fn variant(wg_load: bool) -> Result> { let span = naga::Span::default(); let mut module = Module::default(); let ty_u32 = module.types.insert( @@ -279,7 +286,7 @@ fn builtin_cross_product_args() { fn variant( size: VectorSize, arity: usize, - ) -> Result> { + ) -> Result> { let span = naga::Span::default(); let mut module = Module::default(); let ty_vec3f = module.types.insert( @@ -354,7 +361,6 @@ fn builtin_cross_product_args() { assert!(variant(VectorSize::Quad, 2).is_err()); } -#[cfg(feature = "wgsl-in")] #[test] fn incompatible_interpolation_and_sampling_types() { use dummy_interpolation_shader::DummyInterpolationShader; @@ -436,7 +442,6 @@ fn incompatible_interpolation_and_sampling_types() { } } -#[cfg(all(feature = "wgsl-in", feature = "glsl-out"))] #[test] fn no_flat_first_in_glsl() { use dummy_interpolation_shader::DummyInterpolationShader; @@ -477,13 +482,11 @@ fn no_flat_first_in_glsl() { )); } -#[cfg(all(test, feature = "wgsl-in"))] mod dummy_interpolation_shader { pub struct DummyInterpolationShader { pub source: String, pub module: naga::Module, pub interpolate_attr: String, - #[cfg_attr(not(feature = "glsl-out"), expect(dead_code))] pub entry_point: &'static str, } @@ -541,7 +544,7 @@ fn main(input: VertexOutput) {{ #[allow(dead_code)] struct BindingArrayFixture { - module: naga::Module, + module: Module, span: naga::Span, ty_u32: naga::Handle, ty_array: naga::Handle, @@ -551,7 +554,7 @@ struct BindingArrayFixture { impl BindingArrayFixture { fn new() -> Self { - let mut module = naga::Module::default(); + let mut module = Module::default(); let span = naga::Span::default(); let ty_u32 = module.types.insert( naga::Type { @@ -649,7 +652,6 @@ fn binding_arrays_cannot_hold_scalars() { assert!(t.validator.validate(&t.module).is_err()); } -#[cfg(feature = "wgsl-in")] #[test] fn validation_error_messages() { let cases = [( @@ -686,10 +688,9 @@ error: Function [1] 'main' is invalid } } -#[cfg(feature = "wgsl-in")] #[test] fn bad_texture_dimensions_level() { - fn validate(level: &str) -> Result { + fn validate(level: &str) -> Result { let source = format!( r#" @group(0) @binding(0) @@ -705,9 +706,7 @@ fn bad_texture_dimensions_level() { .map_err(|err| err.into_inner()) // discard spans } - fn is_bad_level_error( - result: Result, - ) -> bool { + fn is_bad_level_error(result: Result) -> bool { matches!( result, Err(naga::valid::ValidationError::Function { @@ -734,7 +733,7 @@ fn arity_check() { use naga::Span; let _ = env_logger::builder().is_test(true).try_init(); - type Result = core::result::Result; + type Result = core::result::Result; fn validate(fun: ir::MathFunction, args: &[usize]) -> Result { let nowhere = Span::default(); @@ -785,7 +784,6 @@ fn arity_check() { assert!(validate(Mf::Pow, &[3]).is_err()); } -#[cfg(feature = "wgsl-in")] #[test] fn global_use_scalar() { let source = " @@ -810,7 +808,6 @@ fn main() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn global_use_array() { let source = " @@ -835,7 +832,6 @@ fn main() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn global_use_array_index() { let source = " @@ -860,7 +856,6 @@ fn main() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn global_use_phony() { let source = " @@ -885,7 +880,6 @@ fn main() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn global_use_unreachable() { // We should allow statements after `return`, and such statements should @@ -997,7 +991,6 @@ fn unused() { .unwrap(); } -#[cfg(feature = "wgsl-in")] #[test] fn override_in_workgroup_size() { override_test( @@ -1010,7 +1003,6 @@ fn used() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn override_in_workgroup_size_nested() { // Initializer for override used in workgroup size refers to another @@ -1027,7 +1019,6 @@ fn used() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn override_in_function() { override_test( @@ -1045,7 +1036,6 @@ fn used() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn override_in_entrypoint() { override_test( @@ -1063,7 +1053,6 @@ fn used() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn override_in_array_size() { override_test( @@ -1079,7 +1068,6 @@ fn used() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn override_in_global_init() { override_test( @@ -1095,7 +1083,6 @@ fn used() { ); } -#[cfg(feature = "wgsl-in")] #[test] fn override_with_multiple_globals() { // Test that when compaction of the `unused` entrypoint removes `arr1`, the diff --git a/naga/tests/naga/wgsl_errors.rs b/naga/tests/naga/wgsl_errors.rs index 962c8f46a2c..675f52b94e1 100644 --- a/naga/tests/naga/wgsl_errors.rs +++ b/naga/tests/naga/wgsl_errors.rs @@ -1,9 +1,20 @@ -/*! -Tests for the WGSL front end. -*/ +//! Tests for the WGSL front end. +//! +//! This file also contains some tests of the module validator. In some cases, +//! the validator and the frontend both raise an error, and it is easier to +//! have both tests in one place. In other cases, it might be more appropriate +//! for the validator tests to be in the `validation` test suite. + #![cfg(feature = "wgsl-in")] +#![allow( + // We need to investigate these. + clippy::result_large_err +)] -use naga::{compact::KeepUnused, valid::Capabilities}; +use naga::{ + compact::KeepUnused, + valid::{self, Capabilities}, +}; #[track_caller] fn check(input: &str, snapshot: &str) { @@ -943,50 +954,6 @@ fn matrix_constructor_inferred() { ); } -#[test] -fn float16_requires_enable() { - check( - r#" - const a: f16 = 1.0; - "#, - r#"error: the `f16` enable extension is not enabled - ┌─ wgsl:2:22 - │ -2 │ const a: f16 = 1.0; - │ ^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. - │ - = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. - -"#, - ); - - check( - r#" - const a = 1.0h; - "#, - r#"error: the `f16` enable extension is not enabled - ┌─ wgsl:2:23 - │ -2 │ const a = 1.0h; - │ ^^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. - │ - = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. - -"#, - ); -} - -#[test] -fn multiple_enables_valid() { - check_success( - r#" - enable f16; - enable f16; - const a: f16 = 1.0h; - "#, - ); -} - /// Check the result of validating a WGSL program against a pattern. /// /// Unless you are generating code programmatically, the @@ -1035,6 +1002,125 @@ macro_rules! check_one_validation { } } +/// Test validation of required extensions and capabilities. +/// +/// This tests that the shader is rejected either if the required extension is +/// not declared in an `enable` directive, or if the validator is configured +/// without the required capability. +/// +/// For the first case, we use the supplied test case source verbatim (which +/// should not include the `enable` directive), and check for a parse error +/// matching the expected error message text. For the second case, we add the +/// `enable` directive to the supplied test case, and check for a validation +/// error matching the expected pattern. +/// +/// The WGSL frontend is not the only way of producing Naga IR, and the +/// validator must reject an invalid module however produced. So it is important +/// that the validator check for missing capabilities. Checking missing +/// extensions in the frontend as well can produce better error messages or +/// simplify implementation of the frontend by eliminating some cases of invalid +/// programs earlier. +/// +/// Multiple capabilities can be specified in the macro argument in the case +/// where any one of them grants access to a feature (e.g. `SUBGROUP` and +/// `SUBGROUP_BARRIER` for `subgroupBarrier`). When passing multiple capabilities, +/// all of the passed capabilities must be covered by the same enable-extension. +/// +/// NOTE: The only reason we don't use a function for this is because we need to syntactically +/// re-use `$val_err_pat`. +macro_rules! check_extension_validation { + ( $caps:expr, $source:expr, $parse_err:expr, $val_err_pat:pat ) => { + let caps = $caps; + let source = $source; + let mut ext = None; + for cap in caps.iter() { + match cap.extension() { + Some(this_ext) if ext.is_none() => ext = Some(this_ext), + Some(this_ext) if ext.is_some_and(|ext| ext != this_ext) => { + panic!( + concat!( + "the capabilities {:?} in `check_extension_validation` ", + "are not all covered by the same extension ", + "(found both {:?} and {:?})", + ), + caps, ext, this_ext, + ); + } + _ => {} + } + } + let Some(ext) = ext else { + panic!( + concat!( + "None of the capabilities {:?} in `check_extension_validation` ", + "are associated with an extension. ", + "Use `check_validation!` to check validator behavior ", + "when there isn't a corresponding parse error.", + ), + caps + ); + }; + let directive = format!( + "enable {};", + naga::front::wgsl::EnableExtension::Implemented(ext).to_ident() + ); + assert!( + !source.contains(&directive), + "test case for `check_extension_validation!` should not contain the enable directive", + ); + + // First check, for the expected WGSL parse error when extension is not enabled + check(&source, $parse_err); + let source_with_enable = format!("{directive}\n{source}"); + let module = match naga::front::wgsl::parse_str(&source_with_enable) { + Ok(module) => module, + Err(err) => { + eprintln!("WGSL parse failed:"); + panic!("{}", err.emit_to_string(source)); + } + }; + + // Second check, for the expected validation error when the capability is not present + let error = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), !caps) + .validate(&module) + .map_err(|e| e.into_inner()); // TODO(https://github.com/gfx-rs/wgpu/issues/8153): Add tests for spans + #[allow(clippy::redundant_pattern_matching)] + if !matches!(&error, $val_err_pat) { + eprintln!( + concat!( + "validation error without {:?} does not match pattern:\n", + "source code: {}\n", + "\n", + "actual result:\n", + "{:#?}\n", + "\n", + "expected match for pattern:\n", + "{}", + ), + caps, + &source, + error, + stringify!($val_err_pat) + ); + panic!("validation error does not match pattern"); + } + + // Also check that when multiple capabililiites can enable a feature, + // any one of them is sufficient. + if !caps.bits().is_power_of_two() { + for cap in caps.iter() { + let res = naga::valid::Validator::new(naga::valid::ValidationFlags::all(), cap) + .validate(&module); + + match res { + Ok(_) => {} + Err(err) => panic!("Module did not validate with only {cap:?}: {err:?}"), + } + } + } + }; +} + macro_rules! check_validation { // We want to support an optional guard expression after the pattern, so // that we can check values we can't match against, like strings. @@ -1077,7 +1163,17 @@ fn validation_error( }; naga::valid::Validator::new(naga::valid::ValidationFlags::all(), caps) .validate(&module) - .map_err(|e| e.into_inner()) // TODO: Add tests for spans, too? + .map_err(|e| e.into_inner()) // TODO(https://github.com/gfx-rs/wgpu/issues/8153): Add tests for spans +} + +/// Check that a shader validates successfully. +/// +/// In a few tests it is useful to check conditions where a validation error +/// should be absent alongside conditions where it should be present. This +/// wrapper is less confusing than `validation_error().unwrap()`. +#[track_caller] +fn no_validation_error(source: &str, caps: naga::valid::Capabilities) { + validation_error(source, caps).unwrap(); } #[test] @@ -1093,12 +1189,136 @@ fn int64_capability() { } #[test] -fn float16_capability() { - check_validation! { - "enable f16; var input: f16;", - "enable f16; var input: vec2;": +fn multiple_enables_valid() { + check_success( + r#" + enable f16; + enable f16; + const a: f16 = 1.0h; + "#, + ); +} + +#[test] +fn float16_capability_and_enable() { + // A zero value expression + check_extension_validation! { + Capabilities::SHADER_FLOAT16, + r#"fn foo() { + let a = f16(); + } + "#, + r#"error: the `f16` enable extension is not enabled + ┌─ wgsl:2:21 + │ +2 │ let a = f16(); + │ ^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. + │ + = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. + +"#, + Err(naga::valid::ValidationError::Type { + source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: "FLOAT16", .. }), + .. + }) + } + + // Literals + check_extension_validation! { + Capabilities::SHADER_FLOAT16, + r#"fn foo() { + let a = f16(1); + } + "#, + r#"error: the `f16` enable extension is not enabled + ┌─ wgsl:2:21 + │ +2 │ let a = f16(1); + │ ^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. + │ + = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. + +"#, + Err(naga::valid::ValidationError::Function { + source: naga::valid::FunctionError::Expression { + source: naga::valid::ExpressionError::Literal( + naga::valid::LiteralError::Width( + naga::valid::WidthError::MissingCapability { flag: "FLOAT16", .. } + ) + ), + .. + }, + .. + }) + } + check_extension_validation! { + Capabilities::SHADER_FLOAT16, + r#" + const a = 1.0h; + "#, + r#"error: the `f16` enable extension is not enabled + ┌─ wgsl:2:23 + │ +2 │ const a = 1.0h; + │ ^^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. + │ + = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. + +"#, Err(naga::valid::ValidationError::Type { - source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability {flag: "FLOAT16",..}), + source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: "FLOAT16", .. }), + .. + }) + } + + // `f16`-typed declarations + check_extension_validation! { + Capabilities::SHADER_FLOAT16, + r#" + const a: f16 = 1.0; + "#, + r#"error: the `f16` enable extension is not enabled + ┌─ wgsl:2:22 + │ +2 │ const a: f16 = 1.0; + │ ^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. + │ + = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. + +"#, + Err(naga::valid::ValidationError::Type { + source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: "FLOAT16", .. }), + .. + }) + } + check_extension_validation! { + Capabilities::SHADER_FLOAT16, + "var input: f16;", + r#"error: the `f16` enable extension is not enabled + ┌─ wgsl:1:12 + │ +1 │ var input: f16; + │ ^^^ the `f16` "Enable Extension" is needed for this functionality, but it is not currently enabled. + │ + = note: You can enable this extension by adding `enable f16;` at the top of the shader, before any other items. + +"#, + Err(naga::valid::ValidationError::Type { + source: naga::valid::TypeError::WidthError(naga::valid::WidthError::MissingCapability { flag: "FLOAT16", .. }), + .. + }) + } + + // Functions that operate on `f16`-precision values stored in `f32`s. + check_validation! { + "fn foo() -> f32 { return quantizeToF16(1.0f); }", + "fn foo() -> u32 { return pack2x16float(vec2(1.0f, 2.0f)); }", + "fn foo() -> vec2 { return unpack2x16float(0x7c007c00); }": + Err(naga::valid::ValidationError::Function { + source: naga::valid::FunctionError::Expression { + source: naga::valid::ExpressionError::MissingCapabilities(Capabilities::SHADER_FLOAT16_IN_FLOAT32), + .. + }, .. }) } @@ -1662,37 +1882,16 @@ fn missing_bindings2() { #[test] fn invalid_blend_src() { - // Missing capability. - check_validation! { + // Missing capability or enable directive + check_extension_validation! { + Capabilities::DUAL_SOURCE_BLENDING, " - enable dual_source_blending; struct FragmentOutput { @location(0) @blend_src(0) output0: vec4, @location(0) @blend_src(1) output1: vec4, } @fragment fn main() -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(1.0)); } - ": - Err( - naga::valid::ValidationError::EntryPoint { - stage: naga::ShaderStage::Fragment, - source: naga::valid::EntryPointError::Result( - naga::valid::VaryingError::UnsupportedCapability(Capabilities::DUAL_SOURCE_BLENDING), - ), - .. - }, - ) - } - - // Missing enable directive. - // Note that this is a parsing error, not a validation error. - check(" - struct FragmentOutput { - @location(0) @blend_src(0) output0: vec4, - @location(0) @blend_src(1) output1: vec4, - } - @fragment - fn main(@builtin(position) position: vec4) -> FragmentOutput { return FragmentOutput(vec4(0.0), vec4(0.0)); } ", r###"error: the `dual_source_blending` enable extension is not enabled ┌─ wgsl:3:27 @@ -1703,7 +1902,16 @@ fn invalid_blend_src() { = note: You can enable this extension by adding `enable dual_source_blending;` at the top of the shader, before any other items. "###, - ); + Err( + naga::valid::ValidationError::EntryPoint { + stage: naga::ShaderStage::Fragment, + source: naga::valid::EntryPointError::Result( + naga::valid::VaryingError::UnsupportedCapability(Capabilities::DUAL_SOURCE_BLENDING), + ), + .. + }, + ) + } // Using blend_src on an input. check_validation! { @@ -2105,7 +2313,7 @@ error: type mismatch for reject and accept values in `select` call │ 5 │ _ = select(true, 1, false); │ ^^^^ ^ accept value of type `{AbstractInt}` - │ │ + │ │\x20\x20\x20\x20\x20\x20 │ reject value of type `bool` ", @@ -3387,6 +3595,7 @@ fn issue7165() { fn invalid_return_type(a: Struct) -> i32 { return a; } "; + // We need the span for the error, so have to invoke manually. let module = naga::front::wgsl::parse_str(shader).unwrap(); let err = naga::valid::Validator::new( naga::valid::ValidationFlags::all(), @@ -3481,19 +3690,19 @@ fn inconsistent_type() { "fn foo() -> f32 { return dot(vec4(), vec3()); }", - r#"error: inconsistent type passed as argument #2 to `dot` + "error: inconsistent type passed as argument #2 to `dot` ┌─ wgsl:2:20 │ 2 │ return dot(vec4(), vec3()); │ ^^^ ^^^^^^^^^^ ^^^^^^^^^^ argument #2 has type vec3 - │ │ + │ │\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20\x20 │ this argument has type vec4, which constrains subsequent arguments │ = note: Because argument #1 has type vec4, only the following types = note: (or types that automatically convert to them) are accepted for argument #2: = note: allowed type: vec4 -"#, +", ); } @@ -3636,6 +3845,171 @@ fn const_eval_value_errors() { assert!(variant("f32(abs(-9223372036854775807 - 1))").is_ok()); } +#[test] +fn subgroup_capability() { + // Some of these tests should be `check_extension_validation` tests that + // also check handling of the enable directive, but that handling is not + // currently correct. https://github.com/gfx-rs/wgpu/issues/8202 + + // Non-barrier subgroup operations... + + // ...in fragment and compute shaders require [`Capabilities::SUBGROUP`]`. + for stage in [naga::ShaderStage::Fragment, naga::ShaderStage::Compute] { + let stage_attr = match stage { + naga::ShaderStage::Fragment => "@fragment", + naga::ShaderStage::Compute => "@compute @workgroup_size(1)", + _ => unreachable!(), + }; + check_one_validation! { + &format!(" + {stage_attr} + fn main() {{ + subgroupBallot(); + }} + "), + Err(naga::valid::ValidationError::EntryPoint { + stage: err_stage, + source: naga::valid::EntryPointError::Function( + naga::valid::FunctionError::MissingCapability(Capabilities::SUBGROUP) + ), + .. + }) if *err_stage == stage + } + } + + // ...in fragment and compute shaders require *only* [`Capabilities::SUBGROUP`]`. + for stage in [naga::ShaderStage::Fragment, naga::ShaderStage::Compute] { + let stage_attr = match stage { + naga::ShaderStage::Fragment => "@fragment", + naga::ShaderStage::Compute => "@compute @workgroup_size(1)", + _ => unreachable!(), + }; + no_validation_error( + &format!( + " + {stage_attr} + fn main() {{ + subgroupBallot(); + }} + " + ), + Capabilities::SUBGROUP, + ); + } + + // ...in vertex shaders require both [`Capabilities::SUBGROUP`] and + // [`Capabilities::SUBGROUP_VERTEX_STAGE`]`. (But note that + // `create_validator` automatically sets `Capabilities::SUBGROUP` whenever + // `Features::SUBGROUP_VERTEX` is available.) + for cap in [Capabilities::SUBGROUP, Capabilities::SUBGROUP_VERTEX_STAGE] { + check_validation! { + " + @vertex + fn main() -> @builtin(position) vec4 {{ + subgroupBallot(); + return vec4(); + }} + ": + Err(_), + cap + } + } + no_validation_error( + " + @vertex + fn main() -> @builtin(position) vec4 {{ + subgroupBallot(); + return vec4(); + }} + ", + Capabilities::SUBGROUP | Capabilities::SUBGROUP_VERTEX_STAGE, + ); + + // Subgroup barriers... + + // ...require both SUBGROUP and SUBGROUP_BARRIER. + for cap in [Capabilities::SUBGROUP, Capabilities::SUBGROUP_BARRIER] { + check_validation! { + r#" + @compute @workgroup_size(1) + fn main() { + subgroupBarrier(); + } + "#: + Err(naga::valid::ValidationError::EntryPoint { + stage: naga::ShaderStage::Compute, + source: naga::valid::EntryPointError::Function( + naga::valid::FunctionError::MissingCapability(required_caps) + ), + .. + }) if *required_caps == Capabilities::SUBGROUP | Capabilities::SUBGROUP_BARRIER, + cap + } + } + + // ...are never supported in vertex shaders. + check_validation! { + r#" + @vertex + fn main() -> @builtin(position) vec4 { + subgroupBarrier(); + return vec4(); + } + "#: + Err(naga::valid::ValidationError::EntryPoint { + stage: naga::ShaderStage::Vertex, + source: naga::valid::EntryPointError::ForbiddenStageOperations, + .. + }), + Capabilities::SUBGROUP | Capabilities::SUBGROUP_BARRIER | Capabilities::SUBGROUP_VERTEX_STAGE + } + + // ...are never supported in fragment shaders. + check_validation! { + r#" + @fragment + fn main() { + subgroupBarrier(); + } + "#: + Err(naga::valid::ValidationError::EntryPoint { + stage: naga::ShaderStage::Fragment, + source: naga::valid::EntryPointError::ForbiddenStageOperations, + .. + }), + Capabilities::SUBGROUP | Capabilities::SUBGROUP_BARRIER + } + + // The `subgroup_id` built-in... + + // ...in compute shaders requires [`Capabilities::SUBGROUP`]`. + check_one_validation! { + " + @compute @workgroup_size(1) + fn main(@builtin(subgroup_id) subgroup_id: u32) {{ + }} + ", + Err(naga::valid::ValidationError::EntryPoint { + stage: naga::ShaderStage::Compute, + source: naga::valid::EntryPointError::Argument( + _, + naga::valid::VaryingError::UnsupportedCapability(Capabilities::SUBGROUP) + ), + .. + }) + } + + // ...in compute shaders requires *only* [`Capabilities::SUBGROUP`]`. + no_validation_error( + " + @compute @workgroup_size(1) + fn main(@builtin(subgroup_id) subgroup_id: u32) {{ + }} + ", + Capabilities::SUBGROUP, + ); +} + #[test] fn subgroup_invalid_broadcast() { check_validation! { @@ -3670,35 +4044,9 @@ fn subgroup_invalid_broadcast() { #[test] fn invalid_clip_distances() { - // Missing capability. - check_validation! { - r#" - enable clip_distances; - struct VertexOutput { - @builtin(position) pos: vec4f, - @builtin(clip_distances) clip_distances: array, - } - - @vertex - fn vs_main() -> VertexOutput { - var out: VertexOutput; - return out; - } - "#: - Err( - naga::valid::ValidationError::EntryPoint { - stage: naga::ShaderStage::Vertex, - source: naga::valid::EntryPointError::Result( - naga::valid::VaryingError::UnsupportedCapability(Capabilities::CLIP_DISTANCE), - ), - .. - }, - ) - } - - // Missing enable directive. - // Note that this is a parsing error, not a validation error. - check( + // Missing capability or enable directive + check_extension_validation! { + Capabilities::CLIP_DISTANCE, r#" @vertex fn vs_main() -> @builtin(clip_distances) array { @@ -3715,7 +4063,14 @@ fn invalid_clip_distances() { = note: You can enable this extension by adding `enable clip_distances;` at the top of the shader, before any other items. "###, - ); + Err(naga::valid::ValidationError::EntryPoint { + stage: naga::ShaderStage::Vertex, + source: naga::valid::EntryPointError::Result( + naga::valid::VaryingError::UnsupportedCapability(Capabilities::CLIP_DISTANCE) + ), + .. + }) + } // Maximum clip distances exceeded check_validation! { @@ -3742,3 +4097,189 @@ fn invalid_clip_distances() { naga::valid::Capabilities::CLIP_DISTANCE } } + +#[test] +fn recognized_but_unimplemented_enable_extension() { + for extension in [ + naga::front::wgsl::UnimplementedEnableExtension::Subgroups, + naga::front::wgsl::UnimplementedEnableExtension::PrimitiveIndex, + ] { + // NOTE: We match exhaustively here to help maintainers add or remove variants to the above + // array. + let snapshot = match extension { + naga::front::wgsl::UnimplementedEnableExtension::Subgroups => "\ +error: the `subgroups` enable-extension is not yet supported + ┌─ wgsl:1:8 + │ +1 │ enable subgroups; + │ ^^^^^^^^^ this enable-extension specifies standard functionality which is not yet implemented in Naga + │ + = note: Let Naga maintainers know that you ran into this at , so they can prioritize it! + +", + naga::front::wgsl::UnimplementedEnableExtension::PrimitiveIndex => "\ +error: the `primitive_index` enable-extension is not yet supported + ┌─ wgsl:1:8 + │ +1 │ enable primitive_index; + │ ^^^^^^^^^^^^^^^ this enable-extension specifies standard functionality which is not yet implemented in Naga + │ + = note: Let Naga maintainers know that you ran into this at , so they can prioritize it! + +", + }; + + let shader = { + let extension = naga::front::wgsl::EnableExtension::Unimplemented(extension); + format!("enable {};", extension.to_ident()) + }; + + check(&shader, snapshot); + } +} + +#[test] +fn max_type_size_large_array() { + // The total size of an array is not resolved until validation. Type aliases + // don't get spans so the error isn't very helpful. + check_validation! { + "alias LargeArray = array;": + Err(naga::valid::ValidationError::Layouter( + naga::proc::LayoutError { + inner: naga::proc::LayoutErrorInner::TooLarge, + .. + } + )) + } +} + +#[test] +fn max_type_size_array_of_arrays() { + // If the size of the base type of an array is oversize, the error is raised + // during lowering. Anonymous types don't get spans so this error isn't very + // helpful. + check( + "alias ArrayOfArrays = array, 22>;", + r#"error: type is too large + = note: the maximum size is 1073741824 bytes + +"#, + ); +} + +#[test] +fn max_type_size_override_array() { + // The validation that occurs after override processing should reject any + // arrays that were overridden to be larger than the maximum size. Type + // aliases don't get spans so the error isn't very helpful. + let source = r#" + override SIZE: u32 = 1; + alias ArrayOfOverrideArrays = array; + + var global: ArrayOfOverrideArrays; + + @compute @workgroup_size(64) + fn main() { + let used = &global; + } + "#; + let module = naga::front::wgsl::parse_str(source).expect("module should parse"); + let info = valid::Validator::new(Default::default(), valid::Capabilities::all()) + .validate(&module) + .expect("module should validate"); + + let overrides = hashbrown::HashMap::from([(String::from("SIZE"), f64::from((1 << 28) + 1))]); + let err = naga::back::pipeline_constants::process_overrides(&module, &info, None, &overrides) + .unwrap_err(); + let naga::back::pipeline_constants::PipelineConstantError::ValidationError(err) = err else { + panic!("expected a validation error, got {err:?}"); + }; + assert!(matches!( + err.into_inner(), + naga::valid::ValidationError::Layouter(naga::proc::LayoutError { + inner: naga::proc::LayoutErrorInner::TooLarge, + .. + }), + )); +} + +#[test] +fn max_type_size_array_in_struct() { + // If a struct member is oversize, the error is raised during lowering. + // For struct members we can associate the error with the member. + check( + r#" + struct ContainsLargeArray { + arr: array, + } + "#, + r#"error: struct member is too large + ┌─ wgsl:3:17 + │ +3 │ arr: array, + │ ^^^ this member exceeds the maximum size + │ + = note: the maximum size is 1073741824 bytes + +"#, + ); +} + +#[test] +fn max_type_size_two_arrays_in_struct() { + // The total size of a struct is checked during lowering. For a struct, + // we can associate the error with the struct itself. + check( + r#" + struct TwoArrays { + arr1: array, + arr2: array, + } + "#, + "error: type is too large + ┌─ wgsl:2:13 + │\x20\x20 +2 │ ╭ struct TwoArrays { +3 │ │ arr1: array, +4 │ │ arr2: array, + │ ╰───────────────────────────────────────────────^ this type exceeds the maximum size + │\x20\x20 + = note: the maximum size is 1073741824 bytes + +", + ); +} + +#[test] +fn max_type_size_array_of_structs() { + // The total size of an array is not resolved until validation. Type aliases + // don't get spans so the error isn't very helpful. + check_validation! { + r#" + struct NotVeryBigStruct { + data: u32, + } + alias BigArrayOfStructs = array; + "#: + Err(naga::valid::ValidationError::Layouter( + naga::proc::LayoutError { + inner: naga::proc::LayoutErrorInner::TooLarge, + .. + } + )) + } +} + +#[test] +fn source_with_control_char() { + check( + "\x07", + "error: expected global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) or the end of the file, found \"\\u{7}\" + ┌─ wgsl:1:1 + │ +1 │ � + │ ^ expected global item (`struct`, `const`, `var`, `alias`, `fn`, `diagnostic`, `enable`, `requires`, `;`) or the end of the file + +", + ); +} diff --git a/naga/tests/out/glsl/spv-do-while.main.Fragment.glsl b/naga/tests/out/glsl/spv-do-while.main.Fragment.glsl index fb8f6e1efa7..a79915b8881 100644 --- a/naga/tests/out/glsl/spv-do-while.main.Fragment.glsl +++ b/naga/tests/out/glsl/spv-do-while.main.Fragment.glsl @@ -4,7 +4,7 @@ precision highp float; precision highp int; -void fb1_(inout bool cond) { +void f_u0028_b1_u003b(inout bool cond) { bool loop_init = true; while(true) { if (!loop_init) { @@ -22,7 +22,7 @@ void fb1_(inout bool cond) { void main_1() { bool param = false; param = false; - fb1_(param); + f_u0028_b1_u003b(param); return; } diff --git a/naga/tests/out/glsl/wgsl-7995-unicode-idents.main.Compute.glsl b/naga/tests/out/glsl/wgsl-7995-unicode-idents.main.Compute.glsl new file mode 100644 index 00000000000..25fb2d7548f --- /dev/null +++ b/naga/tests/out/glsl/wgsl-7995-unicode-idents.main.Compute.glsl @@ -0,0 +1,21 @@ +#version 310 es + +precision highp float; +precision highp int; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +layout(std430) readonly buffer type_block_0Compute { float _group_0_binding_0_cs; }; + + +float compute() { + float _e1 = _group_0_binding_0_cs; + float u03b8_2_ = (_e1 + 9001.0); + return u03b8_2_; +} + +void main() { + float _e0 = compute(); + return; +} + diff --git a/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_i32.Compute.glsl b/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_i32.Compute.glsl index 92d9c4355ff..b45d8299f2d 100644 --- a/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_i32.Compute.glsl +++ b/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_i32.Compute.glsl @@ -5,11 +5,11 @@ precision highp int; layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; }; @@ -50,7 +50,7 @@ void main() { int new = floatBitsToInt((intBitsToFloat(_e14) + 1.0)); uint _e20 = i; int _e22 = old; - _atomic_compare_exchange_resultSint4_ _e23; _e23.old_value = atomicCompSwap(_group_0_binding_0_cs[_e20], _e22, new); + _atomic_compare_exchange_result_Sint_4_ _e23; _e23.old_value = atomicCompSwap(_group_0_binding_0_cs[_e20], _e22, new); _e23.exchanged = (_e23.old_value == _e22); old = _e23.old_value; exchanged = _e23.exchanged; diff --git a/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_u32.Compute.glsl b/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_u32.Compute.glsl index bbb4dea843b..171b9a869fb 100644 --- a/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_u32.Compute.glsl +++ b/naga/tests/out/glsl/wgsl-atomicCompareExchange.test_atomic_compare_exchange_u32.Compute.glsl @@ -5,11 +5,11 @@ precision highp int; layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; }; @@ -50,7 +50,7 @@ void main() { uint new = floatBitsToUint((uintBitsToFloat(_e14) + 1.0)); uint _e20 = i_1; uint _e22 = old_1; - _atomic_compare_exchange_resultUint4_ _e23; _e23.old_value = atomicCompSwap(_group_0_binding_1_cs[_e20], _e22, new); + _atomic_compare_exchange_result_Uint_4_ _e23; _e23.old_value = atomicCompSwap(_group_0_binding_1_cs[_e20], _e22, new); _e23.exchanged = (_e23.old_value == _e22); old_1 = _e23.old_value; exchanged_1 = _e23.exchanged; diff --git a/naga/tests/out/glsl/wgsl-atomicOps.cs_main.Compute.glsl b/naga/tests/out/glsl/wgsl-atomicOps.cs_main.Compute.glsl index 7e1b5061ba1..104f671218d 100644 --- a/naga/tests/out/glsl/wgsl-atomicOps.cs_main.Compute.glsl +++ b/naga/tests/out/glsl/wgsl-atomicOps.cs_main.Compute.glsl @@ -9,11 +9,11 @@ struct Struct { uint atomic_scalar; int atomic_arr[2]; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; }; -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; }; @@ -135,21 +135,21 @@ void main() { int _e295 = atomicExchange(workgroup_atomic_arr[1], 1); uint _e299 = atomicExchange(workgroup_struct.atomic_scalar, 1u); int _e304 = atomicExchange(workgroup_struct.atomic_arr[1], 1); - _atomic_compare_exchange_resultUint4_ _e308; _e308.old_value = atomicCompSwap(_group_0_binding_0_cs, 1u, 2u); + _atomic_compare_exchange_result_Uint_4_ _e308; _e308.old_value = atomicCompSwap(_group_0_binding_0_cs, 1u, 2u); _e308.exchanged = (_e308.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e313; _e313.old_value = atomicCompSwap(_group_0_binding_1_cs[1], 1, 2); + _atomic_compare_exchange_result_Sint_4_ _e313; _e313.old_value = atomicCompSwap(_group_0_binding_1_cs[1], 1, 2); _e313.exchanged = (_e313.old_value == 1); - _atomic_compare_exchange_resultUint4_ _e318; _e318.old_value = atomicCompSwap(_group_0_binding_2_cs.atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Uint_4_ _e318; _e318.old_value = atomicCompSwap(_group_0_binding_2_cs.atomic_scalar, 1u, 2u); _e318.exchanged = (_e318.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e324; _e324.old_value = atomicCompSwap(_group_0_binding_2_cs.atomic_arr[1], 1, 2); + _atomic_compare_exchange_result_Sint_4_ _e324; _e324.old_value = atomicCompSwap(_group_0_binding_2_cs.atomic_arr[1], 1, 2); _e324.exchanged = (_e324.old_value == 1); - _atomic_compare_exchange_resultUint4_ _e328; _e328.old_value = atomicCompSwap(workgroup_atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Uint_4_ _e328; _e328.old_value = atomicCompSwap(workgroup_atomic_scalar, 1u, 2u); _e328.exchanged = (_e328.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e333; _e333.old_value = atomicCompSwap(workgroup_atomic_arr[1], 1, 2); + _atomic_compare_exchange_result_Sint_4_ _e333; _e333.old_value = atomicCompSwap(workgroup_atomic_arr[1], 1, 2); _e333.exchanged = (_e333.old_value == 1); - _atomic_compare_exchange_resultUint4_ _e338; _e338.old_value = atomicCompSwap(workgroup_struct.atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Uint_4_ _e338; _e338.old_value = atomicCompSwap(workgroup_struct.atomic_scalar, 1u, 2u); _e338.exchanged = (_e338.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e344; _e344.old_value = atomicCompSwap(workgroup_struct.atomic_arr[1], 1, 2); + _atomic_compare_exchange_result_Sint_4_ _e344; _e344.old_value = atomicCompSwap(workgroup_struct.atomic_arr[1], 1, 2); _e344.exchanged = (_e344.old_value == 1); return; } diff --git a/naga/tests/out/glsl/wgsl-control-flow.main.Compute.glsl b/naga/tests/out/glsl/wgsl-control-flow.main.Compute.glsl index 6ca55cd7640..ac766ce9059 100644 --- a/naga/tests/out/glsl/wgsl-control-flow.main.Compute.glsl +++ b/naga/tests/out/glsl/wgsl-control-flow.main.Compute.glsl @@ -58,19 +58,44 @@ void control_flow() { } case 2: { pos = 1; - return; + break; } case 3: { pos = 2; + break; + } + case 4: { + break; + } + default: { + pos = 3; + break; + } + } + int _e15 = pos; + switch(_e15) { + case 1: { + pos = 0; return; } + case 2: { + pos = 1; + return; + } + case 3: case 4: { + pos = 2; return; } - default: { + case 5: + case 6: { pos = 3; return; } + default: { + pos = 4; + return; + } } } diff --git a/naga/tests/out/glsl/wgsl-empty-if.comp.Compute.glsl b/naga/tests/out/glsl/wgsl-empty-if.comp.Compute.glsl new file mode 100644 index 00000000000..9300c2802e6 --- /dev/null +++ b/naga/tests/out/glsl/wgsl-empty-if.comp.Compute.glsl @@ -0,0 +1,15 @@ +#version 310 es + +precision highp float; +precision highp int; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + + +void main() { + uvec3 id = gl_GlobalInvocationID; + if ((id.x == 0u)) { + } + return; +} + diff --git a/naga/tests/out/hlsl/spv-do-while.hlsl b/naga/tests/out/hlsl/spv-do-while.hlsl index 5724831de7b..cdd5273a6f3 100644 --- a/naga/tests/out/hlsl/spv-do-while.hlsl +++ b/naga/tests/out/hlsl/spv-do-while.hlsl @@ -1,4 +1,4 @@ -void fb1_(inout bool cond) +void f_u0028_b1_u003b(inout bool cond) { uint2 loop_bound = uint2(4294967295u, 4294967295u); bool loop_init = true; @@ -22,7 +22,7 @@ void main_1() bool param = (bool)0; param = false; - fb1_(param); + f_u0028_b1_u003b(param); return; } diff --git a/naga/tests/out/hlsl/spv-fetch_depth.hlsl b/naga/tests/out/hlsl/spv-fetch_depth.hlsl index 71788135f1a..6dd03da2cbb 100644 --- a/naga/tests/out/hlsl/spv-fetch_depth.hlsl +++ b/naga/tests/out/hlsl/spv-fetch_depth.hlsl @@ -19,7 +19,7 @@ void function() } [numthreads(32, 1, 1)] -void cullfetch_depth() +void cull_fetch_depth() { function(); } diff --git a/naga/tests/out/hlsl/spv-fetch_depth.ron b/naga/tests/out/hlsl/spv-fetch_depth.ron index cb8a85c5f8c..16eac451859 100644 --- a/naga/tests/out/hlsl/spv-fetch_depth.ron +++ b/naga/tests/out/hlsl/spv-fetch_depth.ron @@ -5,7 +5,7 @@ ], compute:[ ( - entry_point:"cullfetch_depth", + entry_point:"cull_fetch_depth", target_profile:"cs_5_1", ), ], diff --git a/naga/tests/out/hlsl/wgsl-7995-unicode-idents.hlsl b/naga/tests/out/hlsl/wgsl-7995-unicode-idents.hlsl new file mode 100644 index 00000000000..3bc8261789f --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-7995-unicode-idents.hlsl @@ -0,0 +1,15 @@ +ByteAddressBuffer asdf : register(t0); + +float compute() +{ + float _e1 = asfloat(asdf.Load(0)); + float u03b8_2_ = (_e1 + 9001.0); + return u03b8_2_; +} + +[numthreads(1, 1, 1)] +void main() +{ + const float _e0 = compute(); + return; +} diff --git a/naga/tests/out/hlsl/wgsl-7995-unicode-idents.ron b/naga/tests/out/hlsl/wgsl-7995-unicode-idents.ron new file mode 100644 index 00000000000..a07b03300b1 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-7995-unicode-idents.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"main", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/hlsl/wgsl-atomicCompareExchange-int64.hlsl b/naga/tests/out/hlsl/wgsl-atomicCompareExchange-int64.hlsl index e0e4ca4021c..5bf723e1fb6 100644 --- a/naga/tests/out/hlsl/wgsl-atomicCompareExchange-int64.hlsl +++ b/naga/tests/out/hlsl/wgsl-atomicCompareExchange-int64.hlsl @@ -5,13 +5,13 @@ struct NagaConstants { }; ConstantBuffer _NagaConstants: register(b0, space1); -struct _atomic_compare_exchange_resultSint8_ { +struct _atomic_compare_exchange_result_Sint_8_ { int64_t old_value; bool exchanged; int _end_pad_0; }; -struct _atomic_compare_exchange_resultUint8_ { +struct _atomic_compare_exchange_result_Uint_8_ { uint64_t old_value; bool exchanged; int _end_pad_0; @@ -63,7 +63,7 @@ void test_atomic_compare_exchange_i64_() int64_t new_ = (_e14 + 10L); uint _e19 = i; int64_t _e21 = old; - _atomic_compare_exchange_resultSint8_ _e22; arr_i64_.InterlockedCompareExchange64(_e19*8, _e21, new_, _e22.old_value); + _atomic_compare_exchange_result_Sint_8_ _e22; arr_i64_.InterlockedCompareExchange64(_e19*8, _e21, new_, _e22.old_value); _e22.exchanged = (_e22.old_value == _e21); old = _e22.old_value; exchanged = _e22.exchanged; @@ -115,7 +115,7 @@ void test_atomic_compare_exchange_u64_() uint64_t new_1 = (_e14 + 10uL); uint _e19 = i_1; uint64_t _e21 = old_1; - _atomic_compare_exchange_resultUint8_ _e22; arr_u64_.InterlockedCompareExchange64(_e19*8, _e21, new_1, _e22.old_value); + _atomic_compare_exchange_result_Uint_8_ _e22; arr_u64_.InterlockedCompareExchange64(_e19*8, _e21, new_1, _e22.old_value); _e22.exchanged = (_e22.old_value == _e21); old_1 = _e22.old_value; exchanged_1 = _e22.exchanged; diff --git a/naga/tests/out/hlsl/wgsl-atomicCompareExchange.hlsl b/naga/tests/out/hlsl/wgsl-atomicCompareExchange.hlsl index d523f7a9d2f..f92c83c5e1f 100644 --- a/naga/tests/out/hlsl/wgsl-atomicCompareExchange.hlsl +++ b/naga/tests/out/hlsl/wgsl-atomicCompareExchange.hlsl @@ -1,9 +1,9 @@ -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; }; @@ -54,7 +54,7 @@ void test_atomic_compare_exchange_i32_() int new_ = asint((asfloat(_e14) + 1.0)); uint _e20 = i; int _e22 = old; - _atomic_compare_exchange_resultSint4_ _e23; arr_i32_.InterlockedCompareExchange(_e20*4, _e22, new_, _e23.old_value); + _atomic_compare_exchange_result_Sint_4_ _e23; arr_i32_.InterlockedCompareExchange(_e20*4, _e22, new_, _e23.old_value); _e23.exchanged = (_e23.old_value == _e22); old = _e23.old_value; exchanged = _e23.exchanged; @@ -106,7 +106,7 @@ void test_atomic_compare_exchange_u32_() uint new_1 = asuint((asfloat(_e14) + 1.0)); uint _e20 = i_1; uint _e22 = old_1; - _atomic_compare_exchange_resultUint4_ _e23; arr_u32_.InterlockedCompareExchange(_e20*4, _e22, new_1, _e23.old_value); + _atomic_compare_exchange_result_Uint_4_ _e23; arr_u32_.InterlockedCompareExchange(_e20*4, _e22, new_1, _e23.old_value); _e23.exchanged = (_e23.old_value == _e22); old_1 = _e23.old_value; exchanged_1 = _e23.exchanged; diff --git a/naga/tests/out/hlsl/wgsl-atomicOps-int64.hlsl b/naga/tests/out/hlsl/wgsl-atomicOps-int64.hlsl index 5c05b4ea9f5..eaebd0f591f 100644 --- a/naga/tests/out/hlsl/wgsl-atomicOps-int64.hlsl +++ b/naga/tests/out/hlsl/wgsl-atomicOps-int64.hlsl @@ -10,13 +10,13 @@ struct Struct { int64_t atomic_arr[2]; }; -struct _atomic_compare_exchange_resultUint8_ { +struct _atomic_compare_exchange_result_Uint_8_ { uint64_t old_value; bool exchanged; int _end_pad_0; }; -struct _atomic_compare_exchange_resultSint8_ { +struct _atomic_compare_exchange_result_Sint_8_ { int64_t old_value; bool exchanged; int _end_pad_0; @@ -126,21 +126,21 @@ void cs_main(uint3 id : SV_GroupThreadID, uint3 __local_invocation_id : SV_Group int64_t _e279; InterlockedExchange(workgroup_atomic_arr[1], 1L, _e279); uint64_t _e283; InterlockedExchange(workgroup_struct.atomic_scalar, 1uL, _e283); int64_t _e288; InterlockedExchange(workgroup_struct.atomic_arr[1], 1L, _e288); - _atomic_compare_exchange_resultUint8_ _e292; storage_atomic_scalar.InterlockedCompareExchange64(0, 1uL, 2uL, _e292.old_value); + _atomic_compare_exchange_result_Uint_8_ _e292; storage_atomic_scalar.InterlockedCompareExchange64(0, 1uL, 2uL, _e292.old_value); _e292.exchanged = (_e292.old_value == 1uL); - _atomic_compare_exchange_resultSint8_ _e297; storage_atomic_arr.InterlockedCompareExchange64(8, 1L, 2L, _e297.old_value); + _atomic_compare_exchange_result_Sint_8_ _e297; storage_atomic_arr.InterlockedCompareExchange64(8, 1L, 2L, _e297.old_value); _e297.exchanged = (_e297.old_value == 1L); - _atomic_compare_exchange_resultUint8_ _e302; storage_struct.InterlockedCompareExchange64(0, 1uL, 2uL, _e302.old_value); + _atomic_compare_exchange_result_Uint_8_ _e302; storage_struct.InterlockedCompareExchange64(0, 1uL, 2uL, _e302.old_value); _e302.exchanged = (_e302.old_value == 1uL); - _atomic_compare_exchange_resultSint8_ _e308; storage_struct.InterlockedCompareExchange64(8+8, 1L, 2L, _e308.old_value); + _atomic_compare_exchange_result_Sint_8_ _e308; storage_struct.InterlockedCompareExchange64(8+8, 1L, 2L, _e308.old_value); _e308.exchanged = (_e308.old_value == 1L); - _atomic_compare_exchange_resultUint8_ _e312; InterlockedCompareExchange(workgroup_atomic_scalar, 1uL, 2uL, _e312.old_value); + _atomic_compare_exchange_result_Uint_8_ _e312; InterlockedCompareExchange(workgroup_atomic_scalar, 1uL, 2uL, _e312.old_value); _e312.exchanged = (_e312.old_value == 1uL); - _atomic_compare_exchange_resultSint8_ _e317; InterlockedCompareExchange(workgroup_atomic_arr[1], 1L, 2L, _e317.old_value); + _atomic_compare_exchange_result_Sint_8_ _e317; InterlockedCompareExchange(workgroup_atomic_arr[1], 1L, 2L, _e317.old_value); _e317.exchanged = (_e317.old_value == 1L); - _atomic_compare_exchange_resultUint8_ _e322; InterlockedCompareExchange(workgroup_struct.atomic_scalar, 1uL, 2uL, _e322.old_value); + _atomic_compare_exchange_result_Uint_8_ _e322; InterlockedCompareExchange(workgroup_struct.atomic_scalar, 1uL, 2uL, _e322.old_value); _e322.exchanged = (_e322.old_value == 1uL); - _atomic_compare_exchange_resultSint8_ _e328; InterlockedCompareExchange(workgroup_struct.atomic_arr[1], 1L, 2L, _e328.old_value); + _atomic_compare_exchange_result_Sint_8_ _e328; InterlockedCompareExchange(workgroup_struct.atomic_arr[1], 1L, 2L, _e328.old_value); _e328.exchanged = (_e328.old_value == 1L); return; } diff --git a/naga/tests/out/hlsl/wgsl-atomicOps.hlsl b/naga/tests/out/hlsl/wgsl-atomicOps.hlsl index 5771c898d94..d32d953a18c 100644 --- a/naga/tests/out/hlsl/wgsl-atomicOps.hlsl +++ b/naga/tests/out/hlsl/wgsl-atomicOps.hlsl @@ -3,12 +3,12 @@ struct Struct { int atomic_arr[2]; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; }; -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; }; @@ -117,21 +117,21 @@ void cs_main(uint3 id : SV_GroupThreadID, uint3 __local_invocation_id : SV_Group int _e295; InterlockedExchange(workgroup_atomic_arr[1], int(1), _e295); uint _e299; InterlockedExchange(workgroup_struct.atomic_scalar, 1u, _e299); int _e304; InterlockedExchange(workgroup_struct.atomic_arr[1], int(1), _e304); - _atomic_compare_exchange_resultUint4_ _e308; storage_atomic_scalar.InterlockedCompareExchange(0, 1u, 2u, _e308.old_value); + _atomic_compare_exchange_result_Uint_4_ _e308; storage_atomic_scalar.InterlockedCompareExchange(0, 1u, 2u, _e308.old_value); _e308.exchanged = (_e308.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e313; storage_atomic_arr.InterlockedCompareExchange(4, int(1), int(2), _e313.old_value); + _atomic_compare_exchange_result_Sint_4_ _e313; storage_atomic_arr.InterlockedCompareExchange(4, int(1), int(2), _e313.old_value); _e313.exchanged = (_e313.old_value == int(1)); - _atomic_compare_exchange_resultUint4_ _e318; storage_struct.InterlockedCompareExchange(0, 1u, 2u, _e318.old_value); + _atomic_compare_exchange_result_Uint_4_ _e318; storage_struct.InterlockedCompareExchange(0, 1u, 2u, _e318.old_value); _e318.exchanged = (_e318.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e324; storage_struct.InterlockedCompareExchange(4+4, int(1), int(2), _e324.old_value); + _atomic_compare_exchange_result_Sint_4_ _e324; storage_struct.InterlockedCompareExchange(4+4, int(1), int(2), _e324.old_value); _e324.exchanged = (_e324.old_value == int(1)); - _atomic_compare_exchange_resultUint4_ _e328; InterlockedCompareExchange(workgroup_atomic_scalar, 1u, 2u, _e328.old_value); + _atomic_compare_exchange_result_Uint_4_ _e328; InterlockedCompareExchange(workgroup_atomic_scalar, 1u, 2u, _e328.old_value); _e328.exchanged = (_e328.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e333; InterlockedCompareExchange(workgroup_atomic_arr[1], int(1), int(2), _e333.old_value); + _atomic_compare_exchange_result_Sint_4_ _e333; InterlockedCompareExchange(workgroup_atomic_arr[1], int(1), int(2), _e333.old_value); _e333.exchanged = (_e333.old_value == int(1)); - _atomic_compare_exchange_resultUint4_ _e338; InterlockedCompareExchange(workgroup_struct.atomic_scalar, 1u, 2u, _e338.old_value); + _atomic_compare_exchange_result_Uint_4_ _e338; InterlockedCompareExchange(workgroup_struct.atomic_scalar, 1u, 2u, _e338.old_value); _e338.exchanged = (_e338.old_value == 1u); - _atomic_compare_exchange_resultSint4_ _e344; InterlockedCompareExchange(workgroup_struct.atomic_arr[1], int(1), int(2), _e344.old_value); + _atomic_compare_exchange_result_Sint_4_ _e344; InterlockedCompareExchange(workgroup_struct.atomic_arr[1], int(1), int(2), _e344.old_value); _e344.exchanged = (_e344.old_value == int(1)); return; } diff --git a/naga/tests/out/hlsl/wgsl-control-flow.hlsl b/naga/tests/out/hlsl/wgsl-control-flow.hlsl index 2798c90cfa9..4c907b68fe3 100644 --- a/naga/tests/out/hlsl/wgsl-control-flow.hlsl +++ b/naga/tests/out/hlsl/wgsl-control-flow.hlsl @@ -49,19 +49,44 @@ void control_flow() } case 2: { pos = int(1); - return; + break; } case 3: { pos = int(2); + break; + } + case 4: { + break; + } + default: { + pos = int(3); + break; + } + } + int _e15 = pos; + switch(_e15) { + case 1: { + pos = int(0); return; } + case 2: { + pos = int(1); + return; + } + case 3: case 4: { + pos = int(2); return; } - default: { + case 5: + case 6: { pos = int(3); return; } + default: { + pos = int(4); + return; + } } } diff --git a/naga/tests/out/hlsl/wgsl-empty-if.hlsl b/naga/tests/out/hlsl/wgsl-empty-if.hlsl new file mode 100644 index 00000000000..6fd5db5e928 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-empty-if.hlsl @@ -0,0 +1,7 @@ +[numthreads(1, 1, 1)] +void comp(uint3 id : SV_DispatchThreadID) +{ + if ((id.x == 0u)) { + } + return; +} diff --git a/naga/tests/out/hlsl/wgsl-empty-if.ron b/naga/tests/out/hlsl/wgsl-empty-if.ron new file mode 100644 index 00000000000..03fe98f84cd --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-empty-if.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"comp", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/hlsl/wgsl-hlsl_mat_cx2.hlsl b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx2.hlsl new file mode 100644 index 00000000000..1de9e069cdc --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx2.hlsl @@ -0,0 +1,372 @@ +typedef struct { float2 _0; float2 _1; } __mat2x2; +float2 __get_col_of_mat2x2(__mat2x2 mat, uint idx) { + switch(idx) { + case 0: { return mat._0; } + case 1: { return mat._1; } + default: { return (float2)0; } + } +} +void __set_col_of_mat2x2(__mat2x2 mat, uint idx, float2 value) { + switch(idx) { + case 0: { mat._0 = value; break; } + case 1: { mat._1 = value; break; } + } +} +void __set_el_of_mat2x2(__mat2x2 mat, uint idx, uint vec_idx, float value) { + switch(idx) { + case 0: { mat._0[vec_idx] = value; break; } + case 1: { mat._1[vec_idx] = value; break; } + } +} + +struct StructWithMat { + float2 m_0; float2 m_1; +}; + +struct StructWithArrayOfStructOfMat { + StructWithMat a[4]; +}; + +RWByteAddressBuffer s_m : register(u0); +cbuffer u_m : register(b1) { __mat2x2 u_m; } +RWByteAddressBuffer s_sm : register(u0, space1); +cbuffer u_sm : register(b1, space1) { StructWithMat u_sm; } +RWByteAddressBuffer s_sasm : register(u0, space2); +cbuffer u_sasm : register(b1, space2) { StructWithArrayOfStructOfMat u_sasm; } + +void access_m() +{ + int idx = int(1); + + int _e3 = idx; + idx = asint(asuint(_e3) - asuint(int(1))); + float2x2 l_s_m = float2x2(asfloat(s_m.Load2(0)), asfloat(s_m.Load2(8))); + float2 l_s_c_c = asfloat(s_m.Load2(0)); + int _e11 = idx; + float2 l_s_c_v = asfloat(s_m.Load2(_e11*8)); + float l_s_e_cc = asfloat(s_m.Load(0+0)); + int _e20 = idx; + float l_s_e_cv = asfloat(s_m.Load(_e20*4+0)); + int _e24 = idx; + float l_s_e_vc = asfloat(s_m.Load(0+_e24*8)); + int _e29 = idx; + int _e31 = idx; + float l_s_e_vv = asfloat(s_m.Load(_e31*4+_e29*8)); + float2x2 l_u_m = ((float2x2)u_m); + float2 l_u_c_c = u_m._0; + int _e40 = idx; + float2 l_u_c_v = __get_col_of_mat2x2(u_m, _e40); + float l_u_e_cc = u_m._0.x; + int _e49 = idx; + float l_u_e_cv = u_m._0[_e49]; + int _e53 = idx; + float l_u_e_vc = __get_col_of_mat2x2(u_m, _e53).x; + int _e58 = idx; + int _e60 = idx; + float l_u_e_vv = __get_col_of_mat2x2(u_m, _e58)[_e60]; + { + float2x2 _value2 = l_u_m; + s_m.Store2(0, asuint(_value2[0])); + s_m.Store2(8, asuint(_value2[1])); + } + s_m.Store2(0, asuint(l_u_c_c)); + int _e67 = idx; + s_m.Store2(_e67*8, asuint(l_u_c_v)); + s_m.Store(0+0, asuint(l_u_e_cc)); + int _e74 = idx; + s_m.Store(_e74*4+0, asuint(l_u_e_cv)); + int _e77 = idx; + s_m.Store(0+_e77*8, asuint(l_u_e_vc)); + int _e81 = idx; + int _e83 = idx; + s_m.Store(_e83*4+_e81*8, asuint(l_u_e_vv)); + return; +} + +StructWithMat ConstructStructWithMat(float2x2 arg0) { + StructWithMat ret = (StructWithMat)0; + ret.m_0 = arg0[0]; + ret.m_1 = arg0[1]; + return ret; +} + +float2x2 GetMatmOnStructWithMat(StructWithMat obj) { + return float2x2(obj.m_0, obj.m_1); +} + +void SetMatmOnStructWithMat(StructWithMat obj, float2x2 mat) { + obj.m_0 = mat[0]; + obj.m_1 = mat[1]; +} + +void SetMatVecmOnStructWithMat(StructWithMat obj, float2 vec, uint mat_idx) { + switch(mat_idx) { + case 0: { obj.m_0 = vec; break; } + case 1: { obj.m_1 = vec; break; } + } +} + +void SetMatScalarmOnStructWithMat(StructWithMat obj, float scalar, uint mat_idx, uint vec_idx) { + switch(mat_idx) { + case 0: { obj.m_0[vec_idx] = scalar; break; } + case 1: { obj.m_1[vec_idx] = scalar; break; } + } +} + +void access_sm() +{ + int idx_1 = int(1); + + int _e3 = idx_1; + idx_1 = asint(asuint(_e3) - asuint(int(1))); + StructWithMat l_s_s = ConstructStructWithMat(float2x2(asfloat(s_sm.Load2(0+0)), asfloat(s_sm.Load2(0+8)))); + float2x2 l_s_m_1 = float2x2(asfloat(s_sm.Load2(0+0)), asfloat(s_sm.Load2(0+8))); + float2 l_s_c_c_1 = asfloat(s_sm.Load2(0+0)); + int _e16 = idx_1; + float2 l_s_c_v_1 = asfloat(s_sm.Load2(_e16*8+0)); + float l_s_e_cc_1 = asfloat(s_sm.Load(0+0+0)); + int _e27 = idx_1; + float l_s_e_cv_1 = asfloat(s_sm.Load(_e27*4+0+0)); + int _e32 = idx_1; + float l_s_e_vc_1 = asfloat(s_sm.Load(0+_e32*8+0)); + int _e38 = idx_1; + int _e40 = idx_1; + float l_s_e_vv_1 = asfloat(s_sm.Load(_e40*4+_e38*8+0)); + StructWithMat l_u_s = u_sm; + float2x2 l_u_m_1 = GetMatmOnStructWithMat(u_sm); + float2 l_u_c_c_1 = GetMatmOnStructWithMat(u_sm)[0]; + int _e54 = idx_1; + float2 l_u_c_v_1 = GetMatmOnStructWithMat(u_sm)[_e54]; + float l_u_e_cc_1 = GetMatmOnStructWithMat(u_sm)[0].x; + int _e65 = idx_1; + float l_u_e_cv_1 = GetMatmOnStructWithMat(u_sm)[0][_e65]; + int _e70 = idx_1; + float l_u_e_vc_1 = GetMatmOnStructWithMat(u_sm)[_e70].x; + int _e76 = idx_1; + int _e78 = idx_1; + float l_u_e_vv_1 = GetMatmOnStructWithMat(u_sm)[_e76][_e78]; + { + StructWithMat _value2 = l_u_s; + { + s_sm.Store2(0+0, asuint(_value2.m_0)); + s_sm.Store2(0+8, asuint(_value2.m_1)); + } + } + { + float2x2 _value2 = l_u_m_1; + s_sm.Store2(0+0, asuint(_value2[0])); + s_sm.Store2(0+8, asuint(_value2[1])); + } + s_sm.Store2(0+0, asuint(l_u_c_c_1)); + int _e89 = idx_1; + s_sm.Store2(_e89*8+0, asuint(l_u_c_v_1)); + s_sm.Store(0+0+0, asuint(l_u_e_cc_1)); + int _e98 = idx_1; + s_sm.Store(_e98*4+0+0, asuint(l_u_e_cv_1)); + int _e102 = idx_1; + s_sm.Store(0+_e102*8+0, asuint(l_u_e_vc_1)); + int _e107 = idx_1; + int _e109 = idx_1; + s_sm.Store(_e109*4+_e107*8+0, asuint(l_u_e_vv_1)); + return; +} + +typedef StructWithMat ret_Constructarray4_StructWithMat_[4]; +ret_Constructarray4_StructWithMat_ Constructarray4_StructWithMat_(StructWithMat arg0, StructWithMat arg1, StructWithMat arg2, StructWithMat arg3) { + StructWithMat ret[4] = { arg0, arg1, arg2, arg3 }; + return ret; +} + +StructWithArrayOfStructOfMat ConstructStructWithArrayOfStructOfMat(StructWithMat arg0[4]) { + StructWithArrayOfStructOfMat ret = (StructWithArrayOfStructOfMat)0; + ret.a = arg0; + return ret; +} + +void access_sasm() +{ + int idx_2 = int(1); + + int _e3 = idx_2; + idx_2 = asint(asuint(_e3) - asuint(int(1))); + StructWithArrayOfStructOfMat l_s_s_1 = ConstructStructWithArrayOfStructOfMat(Constructarray4_StructWithMat_(ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+0+0+0)), asfloat(s_sasm.Load2(0+0+0+8)))), ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+16+0+0)), asfloat(s_sasm.Load2(0+16+0+8)))), ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+32+0+0)), asfloat(s_sasm.Load2(0+32+0+8)))), ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+48+0+0)), asfloat(s_sasm.Load2(0+48+0+8)))))); + StructWithMat l_s_a[4] = Constructarray4_StructWithMat_(ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+0+0+0)), asfloat(s_sasm.Load2(0+0+0+8)))), ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+16+0+0)), asfloat(s_sasm.Load2(0+16+0+8)))), ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+32+0+0)), asfloat(s_sasm.Load2(0+32+0+8)))), ConstructStructWithMat(float2x2(asfloat(s_sasm.Load2(0+48+0+0)), asfloat(s_sasm.Load2(0+48+0+8))))); + float2x2 l_s_m_c = float2x2(asfloat(s_sasm.Load2(0+0+0+0)), asfloat(s_sasm.Load2(0+0+0+8))); + int _e17 = idx_2; + float2x2 l_s_m_v = float2x2(asfloat(s_sasm.Load2(0+_e17*16+0+0)), asfloat(s_sasm.Load2(0+_e17*16+0+8))); + float2 l_s_c_cc = asfloat(s_sasm.Load2(0+0+0+0)); + int _e31 = idx_2; + float2 l_s_c_cv = asfloat(s_sasm.Load2(_e31*8+0+0+0)); + int _e36 = idx_2; + float2 l_s_c_vc = asfloat(s_sasm.Load2(0+0+_e36*16+0)); + int _e43 = idx_2; + int _e46 = idx_2; + float2 l_s_c_vv = asfloat(s_sasm.Load2(_e46*8+0+_e43*16+0)); + float l_s_e_ccc = asfloat(s_sasm.Load(0+0+0+0+0)); + int _e61 = idx_2; + float l_s_e_ccv = asfloat(s_sasm.Load(_e61*4+0+0+0+0)); + int _e68 = idx_2; + float l_s_e_cvc = asfloat(s_sasm.Load(0+_e68*8+0+0+0)); + int _e76 = idx_2; + int _e78 = idx_2; + float l_s_e_cvv = asfloat(s_sasm.Load(_e78*4+_e76*8+0+0+0)); + int _e83 = idx_2; + float l_s_e_vcc = asfloat(s_sasm.Load(0+0+0+_e83*16+0)); + int _e91 = idx_2; + int _e95 = idx_2; + float l_s_e_vcv = asfloat(s_sasm.Load(_e95*4+0+0+_e91*16+0)); + int _e100 = idx_2; + int _e103 = idx_2; + float l_s_e_vvc = asfloat(s_sasm.Load(0+_e103*8+0+_e100*16+0)); + int _e109 = idx_2; + int _e112 = idx_2; + int _e114 = idx_2; + float l_s_e_vvv = asfloat(s_sasm.Load(_e114*4+_e112*8+0+_e109*16+0)); + StructWithArrayOfStructOfMat l_u_s_1 = u_sasm; + StructWithMat l_u_a[4] = u_sasm.a; + float2x2 l_u_m_c = GetMatmOnStructWithMat(u_sasm.a[0]); + int _e129 = idx_2; + float2x2 l_u_m_v = GetMatmOnStructWithMat(u_sasm.a[_e129]); + float2 l_u_c_cc = GetMatmOnStructWithMat(u_sasm.a[0])[0]; + int _e143 = idx_2; + float2 l_u_c_cv = GetMatmOnStructWithMat(u_sasm.a[0])[_e143]; + int _e148 = idx_2; + float2 l_u_c_vc = GetMatmOnStructWithMat(u_sasm.a[_e148])[0]; + int _e155 = idx_2; + int _e158 = idx_2; + float2 l_u_c_vv = GetMatmOnStructWithMat(u_sasm.a[_e155])[_e158]; + float l_u_e_ccc = GetMatmOnStructWithMat(u_sasm.a[0])[0].x; + int _e173 = idx_2; + float l_u_e_ccv = GetMatmOnStructWithMat(u_sasm.a[0])[0][_e173]; + int _e180 = idx_2; + float l_u_e_cvc = GetMatmOnStructWithMat(u_sasm.a[0])[_e180].x; + int _e188 = idx_2; + int _e190 = idx_2; + float l_u_e_cvv = GetMatmOnStructWithMat(u_sasm.a[0])[_e188][_e190]; + int _e195 = idx_2; + float l_u_e_vcc = GetMatmOnStructWithMat(u_sasm.a[_e195])[0].x; + int _e203 = idx_2; + int _e207 = idx_2; + float l_u_e_vcv = GetMatmOnStructWithMat(u_sasm.a[_e203])[0][_e207]; + int _e212 = idx_2; + int _e215 = idx_2; + float l_u_e_vvc = GetMatmOnStructWithMat(u_sasm.a[_e212])[_e215].x; + int _e221 = idx_2; + int _e224 = idx_2; + int _e226 = idx_2; + float l_u_e_vvv = GetMatmOnStructWithMat(u_sasm.a[_e221])[_e224][_e226]; + { + StructWithArrayOfStructOfMat _value2 = l_u_s_1; + { + StructWithMat _value3[4] = _value2.a; + { + StructWithMat _value4 = _value3[0]; + { + s_sasm.Store2(0+0+0+0, asuint(_value4.m_0)); + s_sasm.Store2(0+0+0+8, asuint(_value4.m_1)); + } + } + { + StructWithMat _value4 = _value3[1]; + { + s_sasm.Store2(0+16+0+0, asuint(_value4.m_0)); + s_sasm.Store2(0+16+0+8, asuint(_value4.m_1)); + } + } + { + StructWithMat _value4 = _value3[2]; + { + s_sasm.Store2(0+32+0+0, asuint(_value4.m_0)); + s_sasm.Store2(0+32+0+8, asuint(_value4.m_1)); + } + } + { + StructWithMat _value4 = _value3[3]; + { + s_sasm.Store2(0+48+0+0, asuint(_value4.m_0)); + s_sasm.Store2(0+48+0+8, asuint(_value4.m_1)); + } + } + } + } + { + StructWithMat _value2[4] = l_u_a; + { + StructWithMat _value3 = _value2[0]; + { + s_sasm.Store2(0+0+0+0, asuint(_value3.m_0)); + s_sasm.Store2(0+0+0+8, asuint(_value3.m_1)); + } + } + { + StructWithMat _value3 = _value2[1]; + { + s_sasm.Store2(0+16+0+0, asuint(_value3.m_0)); + s_sasm.Store2(0+16+0+8, asuint(_value3.m_1)); + } + } + { + StructWithMat _value3 = _value2[2]; + { + s_sasm.Store2(0+32+0+0, asuint(_value3.m_0)); + s_sasm.Store2(0+32+0+8, asuint(_value3.m_1)); + } + } + { + StructWithMat _value3 = _value2[3]; + { + s_sasm.Store2(0+48+0+0, asuint(_value3.m_0)); + s_sasm.Store2(0+48+0+8, asuint(_value3.m_1)); + } + } + } + { + float2x2 _value2 = l_u_m_c; + s_sasm.Store2(0+0+0+0, asuint(_value2[0])); + s_sasm.Store2(0+0+0+8, asuint(_value2[1])); + } + int _e238 = idx_2; + { + float2x2 _value2 = l_u_m_v; + s_sasm.Store2(0+_e238*16+0+0, asuint(_value2[0])); + s_sasm.Store2(0+_e238*16+0+8, asuint(_value2[1])); + } + s_sasm.Store2(0+0+0+0, asuint(l_u_c_cc)); + int _e250 = idx_2; + s_sasm.Store2(_e250*8+0+0+0, asuint(l_u_c_cv)); + int _e254 = idx_2; + s_sasm.Store2(0+0+_e254*16+0, asuint(l_u_c_vc)); + int _e260 = idx_2; + int _e263 = idx_2; + s_sasm.Store2(_e263*8+0+_e260*16+0, asuint(l_u_c_vv)); + s_sasm.Store(0+0+0+0+0, asuint(l_u_e_ccc)); + int _e276 = idx_2; + s_sasm.Store(_e276*4+0+0+0+0, asuint(l_u_e_ccv)); + int _e282 = idx_2; + s_sasm.Store(0+_e282*8+0+0+0, asuint(l_u_e_cvc)); + int _e289 = idx_2; + int _e291 = idx_2; + s_sasm.Store(_e291*4+_e289*8+0+0+0, asuint(l_u_e_cvv)); + int _e295 = idx_2; + s_sasm.Store(0+0+0+_e295*16+0, asuint(l_u_e_vcc)); + int _e302 = idx_2; + int _e306 = idx_2; + s_sasm.Store(_e306*4+0+0+_e302*16+0, asuint(l_u_e_vcv)); + int _e310 = idx_2; + int _e313 = idx_2; + s_sasm.Store(0+_e313*8+0+_e310*16+0, asuint(l_u_e_vvc)); + int _e318 = idx_2; + int _e321 = idx_2; + int _e323 = idx_2; + s_sasm.Store(_e323*4+_e321*8+0+_e318*16+0, asuint(l_u_e_vvv)); + return; +} + +[numthreads(1, 1, 1)] +void main() +{ + access_m(); + access_sm(); + access_sasm(); + return; +} diff --git a/naga/tests/out/hlsl/wgsl-hlsl_mat_cx2.ron b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx2.ron new file mode 100644 index 00000000000..a07b03300b1 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx2.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"main", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/hlsl/wgsl-hlsl_mat_cx3.hlsl b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx3.hlsl new file mode 100644 index 00000000000..f90cdff6e1a --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx3.hlsl @@ -0,0 +1,350 @@ +struct StructWithMat { + row_major float3x3 m; + int _end_pad_0; +}; + +struct StructWithArrayOfStructOfMat { + StructWithMat a[4]; +}; + +RWByteAddressBuffer s_m : register(u0); +cbuffer u_m : register(b1) { row_major float3x3 u_m; } +RWByteAddressBuffer s_sm : register(u0, space1); +cbuffer u_sm : register(b1, space1) { StructWithMat u_sm; } +RWByteAddressBuffer s_sasm : register(u0, space2); +cbuffer u_sasm : register(b1, space2) { StructWithArrayOfStructOfMat u_sasm; } + +void access_m() +{ + int idx = int(1); + + int _e3 = idx; + idx = asint(asuint(_e3) - asuint(int(1))); + float3x3 l_s_m = float3x3(asfloat(s_m.Load3(0)), asfloat(s_m.Load3(16)), asfloat(s_m.Load3(32))); + float3 l_s_c_c = asfloat(s_m.Load3(0)); + int _e11 = idx; + float3 l_s_c_v = asfloat(s_m.Load3(_e11*16)); + float l_s_e_cc = asfloat(s_m.Load(0+0)); + int _e20 = idx; + float l_s_e_cv = asfloat(s_m.Load(_e20*4+0)); + int _e24 = idx; + float l_s_e_vc = asfloat(s_m.Load(0+_e24*16)); + int _e29 = idx; + int _e31 = idx; + float l_s_e_vv = asfloat(s_m.Load(_e31*4+_e29*16)); + float3x3 l_u_m = u_m; + float3 l_u_c_c = u_m[0]; + int _e40 = idx; + float3 l_u_c_v = u_m[_e40]; + float l_u_e_cc = u_m[0].x; + int _e49 = idx; + float l_u_e_cv = u_m[0][_e49]; + int _e53 = idx; + float l_u_e_vc = u_m[_e53].x; + int _e58 = idx; + int _e60 = idx; + float l_u_e_vv = u_m[_e58][_e60]; + { + float3x3 _value2 = l_u_m; + s_m.Store3(0, asuint(_value2[0])); + s_m.Store3(16, asuint(_value2[1])); + s_m.Store3(32, asuint(_value2[2])); + } + s_m.Store3(0, asuint(l_u_c_c)); + int _e67 = idx; + s_m.Store3(_e67*16, asuint(l_u_c_v)); + s_m.Store(0+0, asuint(l_u_e_cc)); + int _e74 = idx; + s_m.Store(_e74*4+0, asuint(l_u_e_cv)); + int _e77 = idx; + s_m.Store(0+_e77*16, asuint(l_u_e_vc)); + int _e81 = idx; + int _e83 = idx; + s_m.Store(_e83*4+_e81*16, asuint(l_u_e_vv)); + return; +} + +StructWithMat ConstructStructWithMat(float3x3 arg0) { + StructWithMat ret = (StructWithMat)0; + ret.m = arg0; + return ret; +} + +void access_sm() +{ + int idx_1 = int(1); + + int _e3 = idx_1; + idx_1 = asint(asuint(_e3) - asuint(int(1))); + StructWithMat l_s_s = ConstructStructWithMat(float3x3(asfloat(s_sm.Load3(0+0)), asfloat(s_sm.Load3(0+16)), asfloat(s_sm.Load3(0+32)))); + float3x3 l_s_m_1 = float3x3(asfloat(s_sm.Load3(0+0)), asfloat(s_sm.Load3(0+16)), asfloat(s_sm.Load3(0+32))); + float3 l_s_c_c_1 = asfloat(s_sm.Load3(0+0)); + int _e16 = idx_1; + float3 l_s_c_v_1 = asfloat(s_sm.Load3(_e16*16+0)); + float l_s_e_cc_1 = asfloat(s_sm.Load(0+0+0)); + int _e27 = idx_1; + float l_s_e_cv_1 = asfloat(s_sm.Load(_e27*4+0+0)); + int _e32 = idx_1; + float l_s_e_vc_1 = asfloat(s_sm.Load(0+_e32*16+0)); + int _e38 = idx_1; + int _e40 = idx_1; + float l_s_e_vv_1 = asfloat(s_sm.Load(_e40*4+_e38*16+0)); + StructWithMat l_u_s = u_sm; + float3x3 l_u_m_1 = u_sm.m; + float3 l_u_c_c_1 = u_sm.m[0]; + int _e54 = idx_1; + float3 l_u_c_v_1 = u_sm.m[_e54]; + float l_u_e_cc_1 = u_sm.m[0].x; + int _e65 = idx_1; + float l_u_e_cv_1 = u_sm.m[0][_e65]; + int _e70 = idx_1; + float l_u_e_vc_1 = u_sm.m[_e70].x; + int _e76 = idx_1; + int _e78 = idx_1; + float l_u_e_vv_1 = u_sm.m[_e76][_e78]; + { + StructWithMat _value2 = l_u_s; + { + float3x3 _value3 = _value2.m; + s_sm.Store3(0+0, asuint(_value3[0])); + s_sm.Store3(0+16, asuint(_value3[1])); + s_sm.Store3(0+32, asuint(_value3[2])); + } + } + { + float3x3 _value2 = l_u_m_1; + s_sm.Store3(0+0, asuint(_value2[0])); + s_sm.Store3(0+16, asuint(_value2[1])); + s_sm.Store3(0+32, asuint(_value2[2])); + } + s_sm.Store3(0+0, asuint(l_u_c_c_1)); + int _e89 = idx_1; + s_sm.Store3(_e89*16+0, asuint(l_u_c_v_1)); + s_sm.Store(0+0+0, asuint(l_u_e_cc_1)); + int _e98 = idx_1; + s_sm.Store(_e98*4+0+0, asuint(l_u_e_cv_1)); + int _e102 = idx_1; + s_sm.Store(0+_e102*16+0, asuint(l_u_e_vc_1)); + int _e107 = idx_1; + int _e109 = idx_1; + s_sm.Store(_e109*4+_e107*16+0, asuint(l_u_e_vv_1)); + return; +} + +typedef StructWithMat ret_Constructarray4_StructWithMat_[4]; +ret_Constructarray4_StructWithMat_ Constructarray4_StructWithMat_(StructWithMat arg0, StructWithMat arg1, StructWithMat arg2, StructWithMat arg3) { + StructWithMat ret[4] = { arg0, arg1, arg2, arg3 }; + return ret; +} + +StructWithArrayOfStructOfMat ConstructStructWithArrayOfStructOfMat(StructWithMat arg0[4]) { + StructWithArrayOfStructOfMat ret = (StructWithArrayOfStructOfMat)0; + ret.a = arg0; + return ret; +} + +void access_sasm() +{ + int idx_2 = int(1); + + int _e3 = idx_2; + idx_2 = asint(asuint(_e3) - asuint(int(1))); + StructWithArrayOfStructOfMat l_s_s_1 = ConstructStructWithArrayOfStructOfMat(Constructarray4_StructWithMat_(ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+0+0+0)), asfloat(s_sasm.Load3(0+0+0+16)), asfloat(s_sasm.Load3(0+0+0+32)))), ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+48+0+0)), asfloat(s_sasm.Load3(0+48+0+16)), asfloat(s_sasm.Load3(0+48+0+32)))), ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+96+0+0)), asfloat(s_sasm.Load3(0+96+0+16)), asfloat(s_sasm.Load3(0+96+0+32)))), ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+144+0+0)), asfloat(s_sasm.Load3(0+144+0+16)), asfloat(s_sasm.Load3(0+144+0+32)))))); + StructWithMat l_s_a[4] = Constructarray4_StructWithMat_(ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+0+0+0)), asfloat(s_sasm.Load3(0+0+0+16)), asfloat(s_sasm.Load3(0+0+0+32)))), ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+48+0+0)), asfloat(s_sasm.Load3(0+48+0+16)), asfloat(s_sasm.Load3(0+48+0+32)))), ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+96+0+0)), asfloat(s_sasm.Load3(0+96+0+16)), asfloat(s_sasm.Load3(0+96+0+32)))), ConstructStructWithMat(float3x3(asfloat(s_sasm.Load3(0+144+0+0)), asfloat(s_sasm.Load3(0+144+0+16)), asfloat(s_sasm.Load3(0+144+0+32))))); + float3x3 l_s_m_c = float3x3(asfloat(s_sasm.Load3(0+0+0+0)), asfloat(s_sasm.Load3(0+0+0+16)), asfloat(s_sasm.Load3(0+0+0+32))); + int _e17 = idx_2; + float3x3 l_s_m_v = float3x3(asfloat(s_sasm.Load3(0+_e17*48+0+0)), asfloat(s_sasm.Load3(0+_e17*48+0+16)), asfloat(s_sasm.Load3(0+_e17*48+0+32))); + float3 l_s_c_cc = asfloat(s_sasm.Load3(0+0+0+0)); + int _e31 = idx_2; + float3 l_s_c_cv = asfloat(s_sasm.Load3(_e31*16+0+0+0)); + int _e36 = idx_2; + float3 l_s_c_vc = asfloat(s_sasm.Load3(0+0+_e36*48+0)); + int _e43 = idx_2; + int _e46 = idx_2; + float3 l_s_c_vv = asfloat(s_sasm.Load3(_e46*16+0+_e43*48+0)); + float l_s_e_ccc = asfloat(s_sasm.Load(0+0+0+0+0)); + int _e61 = idx_2; + float l_s_e_ccv = asfloat(s_sasm.Load(_e61*4+0+0+0+0)); + int _e68 = idx_2; + float l_s_e_cvc = asfloat(s_sasm.Load(0+_e68*16+0+0+0)); + int _e76 = idx_2; + int _e78 = idx_2; + float l_s_e_cvv = asfloat(s_sasm.Load(_e78*4+_e76*16+0+0+0)); + int _e83 = idx_2; + float l_s_e_vcc = asfloat(s_sasm.Load(0+0+0+_e83*48+0)); + int _e91 = idx_2; + int _e95 = idx_2; + float l_s_e_vcv = asfloat(s_sasm.Load(_e95*4+0+0+_e91*48+0)); + int _e100 = idx_2; + int _e103 = idx_2; + float l_s_e_vvc = asfloat(s_sasm.Load(0+_e103*16+0+_e100*48+0)); + int _e109 = idx_2; + int _e112 = idx_2; + int _e114 = idx_2; + float l_s_e_vvv = asfloat(s_sasm.Load(_e114*4+_e112*16+0+_e109*48+0)); + StructWithArrayOfStructOfMat l_u_s_1 = u_sasm; + StructWithMat l_u_a[4] = u_sasm.a; + float3x3 l_u_m_c = u_sasm.a[0].m; + int _e129 = idx_2; + float3x3 l_u_m_v = u_sasm.a[_e129].m; + float3 l_u_c_cc = u_sasm.a[0].m[0]; + int _e143 = idx_2; + float3 l_u_c_cv = u_sasm.a[0].m[_e143]; + int _e148 = idx_2; + float3 l_u_c_vc = u_sasm.a[_e148].m[0]; + int _e155 = idx_2; + int _e158 = idx_2; + float3 l_u_c_vv = u_sasm.a[_e155].m[_e158]; + float l_u_e_ccc = u_sasm.a[0].m[0].x; + int _e173 = idx_2; + float l_u_e_ccv = u_sasm.a[0].m[0][_e173]; + int _e180 = idx_2; + float l_u_e_cvc = u_sasm.a[0].m[_e180].x; + int _e188 = idx_2; + int _e190 = idx_2; + float l_u_e_cvv = u_sasm.a[0].m[_e188][_e190]; + int _e195 = idx_2; + float l_u_e_vcc = u_sasm.a[_e195].m[0].x; + int _e203 = idx_2; + int _e207 = idx_2; + float l_u_e_vcv = u_sasm.a[_e203].m[0][_e207]; + int _e212 = idx_2; + int _e215 = idx_2; + float l_u_e_vvc = u_sasm.a[_e212].m[_e215].x; + int _e221 = idx_2; + int _e224 = idx_2; + int _e226 = idx_2; + float l_u_e_vvv = u_sasm.a[_e221].m[_e224][_e226]; + { + StructWithArrayOfStructOfMat _value2 = l_u_s_1; + { + StructWithMat _value3[4] = _value2.a; + { + StructWithMat _value4 = _value3[0]; + { + float3x3 _value5 = _value4.m; + s_sasm.Store3(0+0+0+0, asuint(_value5[0])); + s_sasm.Store3(0+0+0+16, asuint(_value5[1])); + s_sasm.Store3(0+0+0+32, asuint(_value5[2])); + } + } + { + StructWithMat _value4 = _value3[1]; + { + float3x3 _value5 = _value4.m; + s_sasm.Store3(0+48+0+0, asuint(_value5[0])); + s_sasm.Store3(0+48+0+16, asuint(_value5[1])); + s_sasm.Store3(0+48+0+32, asuint(_value5[2])); + } + } + { + StructWithMat _value4 = _value3[2]; + { + float3x3 _value5 = _value4.m; + s_sasm.Store3(0+96+0+0, asuint(_value5[0])); + s_sasm.Store3(0+96+0+16, asuint(_value5[1])); + s_sasm.Store3(0+96+0+32, asuint(_value5[2])); + } + } + { + StructWithMat _value4 = _value3[3]; + { + float3x3 _value5 = _value4.m; + s_sasm.Store3(0+144+0+0, asuint(_value5[0])); + s_sasm.Store3(0+144+0+16, asuint(_value5[1])); + s_sasm.Store3(0+144+0+32, asuint(_value5[2])); + } + } + } + } + { + StructWithMat _value2[4] = l_u_a; + { + StructWithMat _value3 = _value2[0]; + { + float3x3 _value4 = _value3.m; + s_sasm.Store3(0+0+0+0, asuint(_value4[0])); + s_sasm.Store3(0+0+0+16, asuint(_value4[1])); + s_sasm.Store3(0+0+0+32, asuint(_value4[2])); + } + } + { + StructWithMat _value3 = _value2[1]; + { + float3x3 _value4 = _value3.m; + s_sasm.Store3(0+48+0+0, asuint(_value4[0])); + s_sasm.Store3(0+48+0+16, asuint(_value4[1])); + s_sasm.Store3(0+48+0+32, asuint(_value4[2])); + } + } + { + StructWithMat _value3 = _value2[2]; + { + float3x3 _value4 = _value3.m; + s_sasm.Store3(0+96+0+0, asuint(_value4[0])); + s_sasm.Store3(0+96+0+16, asuint(_value4[1])); + s_sasm.Store3(0+96+0+32, asuint(_value4[2])); + } + } + { + StructWithMat _value3 = _value2[3]; + { + float3x3 _value4 = _value3.m; + s_sasm.Store3(0+144+0+0, asuint(_value4[0])); + s_sasm.Store3(0+144+0+16, asuint(_value4[1])); + s_sasm.Store3(0+144+0+32, asuint(_value4[2])); + } + } + } + { + float3x3 _value2 = l_u_m_c; + s_sasm.Store3(0+0+0+0, asuint(_value2[0])); + s_sasm.Store3(0+0+0+16, asuint(_value2[1])); + s_sasm.Store3(0+0+0+32, asuint(_value2[2])); + } + int _e238 = idx_2; + { + float3x3 _value2 = l_u_m_v; + s_sasm.Store3(0+_e238*48+0+0, asuint(_value2[0])); + s_sasm.Store3(0+_e238*48+0+16, asuint(_value2[1])); + s_sasm.Store3(0+_e238*48+0+32, asuint(_value2[2])); + } + s_sasm.Store3(0+0+0+0, asuint(l_u_c_cc)); + int _e250 = idx_2; + s_sasm.Store3(_e250*16+0+0+0, asuint(l_u_c_cv)); + int _e254 = idx_2; + s_sasm.Store3(0+0+_e254*48+0, asuint(l_u_c_vc)); + int _e260 = idx_2; + int _e263 = idx_2; + s_sasm.Store3(_e263*16+0+_e260*48+0, asuint(l_u_c_vv)); + s_sasm.Store(0+0+0+0+0, asuint(l_u_e_ccc)); + int _e276 = idx_2; + s_sasm.Store(_e276*4+0+0+0+0, asuint(l_u_e_ccv)); + int _e282 = idx_2; + s_sasm.Store(0+_e282*16+0+0+0, asuint(l_u_e_cvc)); + int _e289 = idx_2; + int _e291 = idx_2; + s_sasm.Store(_e291*4+_e289*16+0+0+0, asuint(l_u_e_cvv)); + int _e295 = idx_2; + s_sasm.Store(0+0+0+_e295*48+0, asuint(l_u_e_vcc)); + int _e302 = idx_2; + int _e306 = idx_2; + s_sasm.Store(_e306*4+0+0+_e302*48+0, asuint(l_u_e_vcv)); + int _e310 = idx_2; + int _e313 = idx_2; + s_sasm.Store(0+_e313*16+0+_e310*48+0, asuint(l_u_e_vvc)); + int _e318 = idx_2; + int _e321 = idx_2; + int _e323 = idx_2; + s_sasm.Store(_e323*4+_e321*16+0+_e318*48+0, asuint(l_u_e_vvv)); + return; +} + +[numthreads(1, 1, 1)] +void main() +{ + access_m(); + access_sm(); + access_sasm(); + return; +} diff --git a/naga/tests/out/hlsl/wgsl-hlsl_mat_cx3.ron b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx3.ron new file mode 100644 index 00000000000..a07b03300b1 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-hlsl_mat_cx3.ron @@ -0,0 +1,12 @@ +( + vertex:[ + ], + fragment:[ + ], + compute:[ + ( + entry_point:"main", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/hlsl/wgsl-texture-external.hlsl b/naga/tests/out/hlsl/wgsl-texture-external.hlsl new file mode 100644 index 00000000000..59e8e62cd72 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-texture-external.hlsl @@ -0,0 +1,174 @@ +struct NagaExternalTextureTransferFn { + float a; + float b; + float g; + float k; +}; + +struct NagaExternalTextureParams { + row_major float4x4 yuv_conversion_matrix; + row_major float3x3 gamut_conversion_matrix; + int _pad2_0; + NagaExternalTextureTransferFn src_tf; + NagaExternalTextureTransferFn dst_tf; + float2 sample_transform_0; float2 sample_transform_1; float2 sample_transform_2; + float2 load_transform_0; float2 load_transform_1; float2 load_transform_2; + uint2 size; + uint num_planes; + int _end_pad_0; +}; + +Texture2D tex_plane0_: register(t0); +Texture2D tex_plane1_: register(t1); +Texture2D tex_plane2_: register(t2); +cbuffer tex_params: register(b3) { NagaExternalTextureParams tex_params; }; +SamplerState nagaSamplerHeap[2048]: register(s0, space0); +SamplerComparisonState nagaComparisonSamplerHeap[2048]: register(s0, space1); +StructuredBuffer nagaGroup0SamplerIndexArray : register(t0, space255); +static const SamplerState samp = nagaSamplerHeap[nagaGroup0SamplerIndexArray[0]]; + +float4 nagaTextureSampleBaseClampToEdge( + Texture2D plane0, + Texture2D plane1, + Texture2D plane2, + NagaExternalTextureParams params, + SamplerState samp, + float2 coords) +{ + float2 plane0_size; + plane0.GetDimensions(plane0_size.x, plane0_size.y); + float3x2 sample_transform = float3x2( + params.sample_transform_0, + params.sample_transform_1, + params.sample_transform_2 + ); + coords = mul(float3(coords, 1.0), sample_transform); + float2 bounds_min = mul(float3(0.0, 0.0, 1.0), sample_transform); + float2 bounds_max = mul(float3(1.0, 1.0, 1.0), sample_transform); + float4 bounds = float4(min(bounds_min, bounds_max), max(bounds_min, bounds_max)); + float2 plane0_half_texel = float2(0.5, 0.5) / plane0_size; + float2 plane0_coords = clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel); + if (params.num_planes == 1u) { + return plane0.SampleLevel(samp, plane0_coords, 0.0f); + } else { + float2 plane1_size; + plane1.GetDimensions(plane1_size.x, plane1_size.y); + float2 plane1_half_texel = float2(0.5, 0.5) / plane1_size; + float2 plane1_coords = clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel); + float y = plane0.SampleLevel(samp, plane0_coords, 0.0f).x; + float2 uv; + if (params.num_planes == 2u) { + uv = plane1.SampleLevel(samp, plane1_coords, 0.0f).xy; + } else { + float2 plane2_size; + plane2.GetDimensions(plane2_size.x, plane2_size.y); + float2 plane2_half_texel = float2(0.5, 0.5) / plane2_size; + float2 plane2_coords = clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane2_half_texel); + uv = float2(plane1.SampleLevel(samp, plane1_coords, 0.0f).x, plane2.SampleLevel(samp, plane2_coords, 0.0f).x); + } + float3 srcGammaRgb = mul(float4(y, uv, 1.0), params.yuv_conversion_matrix).rgb; + float3 srcLinearRgb = srcGammaRgb < params.src_tf.k * params.src_tf.b ? + srcGammaRgb / params.src_tf.k : + pow((srcGammaRgb + params.src_tf.a - 1.0) / params.src_tf.a, params.src_tf.g); + float3 dstLinearRgb = mul(srcLinearRgb, params.gamut_conversion_matrix); + float3 dstGammaRgb = dstLinearRgb < params.dst_tf.b ? + params.dst_tf.k * dstLinearRgb : + params.dst_tf.a * pow(dstLinearRgb, 1.0 / params.dst_tf.g) - (params.dst_tf.a - 1); + return float4(dstGammaRgb, 1.0); + } +} + +float4 nagaTextureLoadExternal( + Texture2D plane0, + Texture2D plane1, + Texture2D plane2, + NagaExternalTextureParams params, + uint2 coords) +{ + uint2 plane0_size; + plane0.GetDimensions(plane0_size.x, plane0_size.y); + uint2 cropped_size = any(params.size) ? params.size : plane0_size; + coords = min(coords, cropped_size - 1); + float3x2 load_transform = float3x2( + params.load_transform_0, + params.load_transform_1, + params.load_transform_2 + ); + uint2 plane0_coords = uint2(round(mul(float3(coords, 1.0), load_transform))); + if (params.num_planes == 1u) { + return plane0.Load(uint3(plane0_coords, 0u)); + } else { + uint2 plane1_size; + plane1.GetDimensions(plane1_size.x, plane1_size.y); + uint2 plane1_coords = uint2(floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size))); + float y = plane0.Load(uint3(plane0_coords, 0u)).x; + float2 uv; + if (params.num_planes == 2u) { + uv = plane1.Load(uint3(plane1_coords, 0u)).xy; + } else { + uint2 plane2_size; + plane2.GetDimensions(plane2_size.x, plane2_size.y); + uint2 plane2_coords = uint2(floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size))); + uv = float2(plane1.Load(uint3(plane1_coords, 0u)).x, plane2.Load(uint3(plane2_coords, 0u)).x); + } + float3 srcGammaRgb = mul(float4(y, uv, 1.0), params.yuv_conversion_matrix).rgb; + float3 srcLinearRgb = srcGammaRgb < params.src_tf.k * params.src_tf.b ? + srcGammaRgb / params.src_tf.k : + pow((srcGammaRgb + params.src_tf.a - 1.0) / params.src_tf.a, params.src_tf.g); + float3 dstLinearRgb = mul(srcLinearRgb, params.gamut_conversion_matrix); + float3 dstGammaRgb = dstLinearRgb < params.dst_tf.b ? + params.dst_tf.k * dstLinearRgb : + params.dst_tf.a * pow(dstLinearRgb, 1.0 / params.dst_tf.g) - (params.dst_tf.a - 1); + return float4(dstGammaRgb, 1.0); + } +} + +uint2 NagaExternalDimensions2D(Texture2D plane0, Texture2D plane1, Texture2D plane2, NagaExternalTextureParams params) { + if (any(params.size)) { + return params.size; + } else { + uint2 ret; + plane0.GetDimensions(ret.x, ret.y); + return ret; + } +} + +float4 test(Texture2D t_plane0_, Texture2D t_plane1_, Texture2D t_plane2_, NagaExternalTextureParams t_params) +{ + float4 a = (float4)0; + float4 b = (float4)0; + float4 c = (float4)0; + uint2 d = (uint2)0; + + float4 _e4 = nagaTextureSampleBaseClampToEdge(t_plane0_, t_plane1_, t_plane2_, t_params, samp, (0.0).xx); + a = _e4; + float4 _e8 = nagaTextureLoadExternal(t_plane0_, t_plane1_, t_plane2_, t_params, (int(0)).xx); + b = _e8; + float4 _e12 = nagaTextureLoadExternal(t_plane0_, t_plane1_, t_plane2_, t_params, (0u).xx); + c = _e12; + d = NagaExternalDimensions2D(t_plane0_, t_plane1_, t_plane2_, t_params); + float4 _e16 = a; + float4 _e17 = b; + float4 _e19 = c; + uint2 _e21 = d; + return (((_e16 + _e17) + _e19) + float2(_e21).xyxy); +} + +float4 fragment_main() : SV_Target0 +{ + const float4 _e1 = test(tex_plane0_, tex_plane1_, tex_plane2_, tex_params); + return _e1; +} + +float4 vertex_main() : SV_Position +{ + const float4 _e1 = test(tex_plane0_, tex_plane1_, tex_plane2_, tex_params); + return _e1; +} + +[numthreads(1, 1, 1)] +void compute_main() +{ + const float4 _e1 = test(tex_plane0_, tex_plane1_, tex_plane2_, tex_params); + return; +} diff --git a/naga/tests/out/hlsl/wgsl-texture-external.ron b/naga/tests/out/hlsl/wgsl-texture-external.ron new file mode 100644 index 00000000000..23afa21e1f5 --- /dev/null +++ b/naga/tests/out/hlsl/wgsl-texture-external.ron @@ -0,0 +1,20 @@ +( + vertex:[ + ( + entry_point:"vertex_main", + target_profile:"vs_5_1", + ), + ], + fragment:[ + ( + entry_point:"fragment_main", + target_profile:"ps_5_1", + ), + ], + compute:[ + ( + entry_point:"compute_main", + target_profile:"cs_5_1", + ), + ], +) diff --git a/naga/tests/out/ir/spv-fetch_depth.compact.ron b/naga/tests/out/ir/spv-fetch_depth.compact.ron index 024f918022c..1fbee2deb35 100644 --- a/naga/tests/out/ir/spv-fetch_depth.compact.ron +++ b/naga/tests/out/ir/spv-fetch_depth.compact.ron @@ -67,6 +67,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/spv-fetch_depth.ron b/naga/tests/out/ir/spv-fetch_depth.ron index 8daa29847ff..186f78354ad 100644 --- a/naga/tests/out/ir/spv-fetch_depth.ron +++ b/naga/tests/out/ir/spv-fetch_depth.ron @@ -130,6 +130,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/spv-shadow.compact.ron b/naga/tests/out/ir/spv-shadow.compact.ron index 7f4e6df5c1a..b49cd9b55be 100644 --- a/naga/tests/out/ir/spv-shadow.compact.ron +++ b/naga/tests/out/ir/spv-shadow.compact.ron @@ -155,6 +155,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/spv-shadow.ron b/naga/tests/out/ir/spv-shadow.ron index 35a9e48befc..e1f0f60b6bb 100644 --- a/naga/tests/out/ir/spv-shadow.ron +++ b/naga/tests/out/ir/spv-shadow.ron @@ -278,6 +278,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/spv-spec-constants.compact.ron b/naga/tests/out/ir/spv-spec-constants.compact.ron index 698a8072584..3fa6ffef4ff 100644 --- a/naga/tests/out/ir/spv-spec-constants.compact.ron +++ b/naga/tests/out/ir/spv-spec-constants.compact.ron @@ -171,6 +171,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/spv-spec-constants.ron b/naga/tests/out/ir/spv-spec-constants.ron index 5afd71ed657..94c90aa78f9 100644 --- a/naga/tests/out/ir/spv-spec-constants.ron +++ b/naga/tests/out/ir/spv-spec-constants.ron @@ -262,6 +262,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/wgsl-access.compact.ron b/naga/tests/out/ir/wgsl-access.compact.ron index 5e15ff84caf..30e88984f3c 100644 --- a/naga/tests/out/ir/wgsl-access.compact.ron +++ b/naga/tests/out/ir/wgsl-access.compact.ron @@ -421,6 +421,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-access.ron b/naga/tests/out/ir/wgsl-access.ron index 5e15ff84caf..30e88984f3c 100644 --- a/naga/tests/out/ir/wgsl-access.ron +++ b/naga/tests/out/ir/wgsl-access.ron @@ -421,6 +421,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-collatz.compact.ron b/naga/tests/out/ir/wgsl-collatz.compact.ron index ce0d6d8c89b..f72c652d032 100644 --- a/naga/tests/out/ir/wgsl-collatz.compact.ron +++ b/naga/tests/out/ir/wgsl-collatz.compact.ron @@ -44,6 +44,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-collatz.ron b/naga/tests/out/ir/wgsl-collatz.ron index ce0d6d8c89b..f72c652d032 100644 --- a/naga/tests/out/ir/wgsl-collatz.ron +++ b/naga/tests/out/ir/wgsl-collatz.ron @@ -44,6 +44,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-const_assert.compact.ron b/naga/tests/out/ir/wgsl-const_assert.compact.ron index c10c9f97d32..2816364f88b 100644 --- a/naga/tests/out/ir/wgsl-const_assert.compact.ron +++ b/naga/tests/out/ir/wgsl-const_assert.compact.ron @@ -4,6 +4,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-const_assert.ron b/naga/tests/out/ir/wgsl-const_assert.ron index c10c9f97d32..2816364f88b 100644 --- a/naga/tests/out/ir/wgsl-const_assert.ron +++ b/naga/tests/out/ir/wgsl-const_assert.ron @@ -4,6 +4,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-diagnostic-filter.compact.ron b/naga/tests/out/ir/wgsl-diagnostic-filter.compact.ron index dc4d2defdbd..c5746696d52 100644 --- a/naga/tests/out/ir/wgsl-diagnostic-filter.compact.ron +++ b/naga/tests/out/ir/wgsl-diagnostic-filter.compact.ron @@ -4,6 +4,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-diagnostic-filter.ron b/naga/tests/out/ir/wgsl-diagnostic-filter.ron index dc4d2defdbd..c5746696d52 100644 --- a/naga/tests/out/ir/wgsl-diagnostic-filter.ron +++ b/naga/tests/out/ir/wgsl-diagnostic-filter.ron @@ -4,6 +4,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-index-by-value.compact.ron b/naga/tests/out/ir/wgsl-index-by-value.compact.ron index 8f3de4f3e2e..a4f84a7a6b2 100644 --- a/naga/tests/out/ir/wgsl-index-by-value.compact.ron +++ b/naga/tests/out/ir/wgsl-index-by-value.compact.ron @@ -81,6 +81,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-index-by-value.ron b/naga/tests/out/ir/wgsl-index-by-value.ron index 8f3de4f3e2e..a4f84a7a6b2 100644 --- a/naga/tests/out/ir/wgsl-index-by-value.ron +++ b/naga/tests/out/ir/wgsl-index-by-value.ron @@ -81,6 +81,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-local-const.compact.ron b/naga/tests/out/ir/wgsl-local-const.compact.ron index 8bce0bb0084..512972657ed 100644 --- a/naga/tests/out/ir/wgsl-local-const.compact.ron +++ b/naga/tests/out/ir/wgsl-local-const.compact.ron @@ -26,6 +26,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/wgsl-local-const.ron b/naga/tests/out/ir/wgsl-local-const.ron index 8bce0bb0084..512972657ed 100644 --- a/naga/tests/out/ir/wgsl-local-const.ron +++ b/naga/tests/out/ir/wgsl-local-const.ron @@ -26,6 +26,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/wgsl-must-use.compact.ron b/naga/tests/out/ir/wgsl-must-use.compact.ron index 0534e736397..a701a6805da 100644 --- a/naga/tests/out/ir/wgsl-must-use.compact.ron +++ b/naga/tests/out/ir/wgsl-must-use.compact.ron @@ -12,6 +12,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-must-use.ron b/naga/tests/out/ir/wgsl-must-use.ron index 0534e736397..a701a6805da 100644 --- a/naga/tests/out/ir/wgsl-must-use.ron +++ b/naga/tests/out/ir/wgsl-must-use.ron @@ -12,6 +12,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.compact.ron b/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.compact.ron index ac45d5bad6a..640ee25ca49 100644 --- a/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.compact.ron +++ b/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.compact.ron @@ -53,6 +53,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: { AtomicCompareExchangeWeakResult(( kind: Uint, diff --git a/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.ron b/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.ron index ac45d5bad6a..640ee25ca49 100644 --- a/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.ron +++ b/naga/tests/out/ir/wgsl-overrides-atomicCompareExchangeWeak.ron @@ -53,6 +53,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: { AtomicCompareExchangeWeakResult(( kind: Uint, diff --git a/naga/tests/out/ir/wgsl-overrides-ray-query.compact.ron b/naga/tests/out/ir/wgsl-overrides-ray-query.compact.ron index 0848ed2b73c..f65e8f186db 100644 --- a/naga/tests/out/ir/wgsl-overrides-ray-query.compact.ron +++ b/naga/tests/out/ir/wgsl-overrides-ray-query.compact.ron @@ -85,6 +85,8 @@ ray_desc: Some(5), ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-overrides-ray-query.ron b/naga/tests/out/ir/wgsl-overrides-ray-query.ron index 0848ed2b73c..f65e8f186db 100644 --- a/naga/tests/out/ir/wgsl-overrides-ray-query.ron +++ b/naga/tests/out/ir/wgsl-overrides-ray-query.ron @@ -85,6 +85,8 @@ ray_desc: Some(5), ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-overrides.compact.ron b/naga/tests/out/ir/wgsl-overrides.compact.ron index 4c40ef32de7..81221ff7941 100644 --- a/naga/tests/out/ir/wgsl-overrides.compact.ron +++ b/naga/tests/out/ir/wgsl-overrides.compact.ron @@ -26,6 +26,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-overrides.ron b/naga/tests/out/ir/wgsl-overrides.ron index 4c40ef32de7..81221ff7941 100644 --- a/naga/tests/out/ir/wgsl-overrides.ron +++ b/naga/tests/out/ir/wgsl-overrides.ron @@ -26,6 +26,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-storage-textures.compact.ron b/naga/tests/out/ir/wgsl-storage-textures.compact.ron index 4f1325da561..ec63fecac27 100644 --- a/naga/tests/out/ir/wgsl-storage-textures.compact.ron +++ b/naga/tests/out/ir/wgsl-storage-textures.compact.ron @@ -71,6 +71,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-storage-textures.ron b/naga/tests/out/ir/wgsl-storage-textures.ron index 4f1325da561..ec63fecac27 100644 --- a/naga/tests/out/ir/wgsl-storage-textures.ron +++ b/naga/tests/out/ir/wgsl-storage-textures.ron @@ -71,6 +71,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-template-list-trailing-comma.compact.ron b/naga/tests/out/ir/wgsl-template-list-trailing-comma.compact.ron index 67ec4ffb52d..a8208c09b86 100644 --- a/naga/tests/out/ir/wgsl-template-list-trailing-comma.compact.ron +++ b/naga/tests/out/ir/wgsl-template-list-trailing-comma.compact.ron @@ -28,6 +28,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-template-list-trailing-comma.ron b/naga/tests/out/ir/wgsl-template-list-trailing-comma.ron index 67ec4ffb52d..a8208c09b86 100644 --- a/naga/tests/out/ir/wgsl-template-list-trailing-comma.ron +++ b/naga/tests/out/ir/wgsl-template-list-trailing-comma.ron @@ -28,6 +28,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [], diff --git a/naga/tests/out/ir/wgsl-texture-external.compact.ron b/naga/tests/out/ir/wgsl-texture-external.compact.ron new file mode 100644 index 00000000000..2b9e1c8d5e4 --- /dev/null +++ b/naga/tests/out/ir/wgsl-texture-external.compact.ron @@ -0,0 +1,491 @@ +( + types: [ + ( + name: None, + inner: Scalar(( + kind: Float, + width: 4, + )), + ), + ( + name: None, + inner: Scalar(( + kind: Uint, + width: 4, + )), + ), + ( + name: None, + inner: Vector( + size: Bi, + scalar: ( + kind: Uint, + width: 4, + ), + ), + ), + ( + name: None, + inner: Matrix( + columns: Tri, + rows: Bi, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ( + name: None, + inner: Matrix( + columns: Tri, + rows: Tri, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ( + name: None, + inner: Matrix( + columns: Quad, + rows: Quad, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ( + name: Some("NagaExternalTextureTransferFn"), + inner: Struct( + members: [ + ( + name: Some("a"), + ty: 0, + binding: None, + offset: 0, + ), + ( + name: Some("b"), + ty: 0, + binding: None, + offset: 4, + ), + ( + name: Some("g"), + ty: 0, + binding: None, + offset: 8, + ), + ( + name: Some("k"), + ty: 0, + binding: None, + offset: 12, + ), + ], + span: 16, + ), + ), + ( + name: Some("NagaExternalTextureParams"), + inner: Struct( + members: [ + ( + name: Some("yuv_conversion_matrix"), + ty: 5, + binding: None, + offset: 0, + ), + ( + name: Some("gamut_conversion_matrix"), + ty: 4, + binding: None, + offset: 64, + ), + ( + name: Some("src_tf"), + ty: 6, + binding: None, + offset: 112, + ), + ( + name: Some("dst_tf"), + ty: 6, + binding: None, + offset: 128, + ), + ( + name: Some("sample_transform"), + ty: 3, + binding: None, + offset: 144, + ), + ( + name: Some("load_transform"), + ty: 3, + binding: None, + offset: 168, + ), + ( + name: Some("size"), + ty: 2, + binding: None, + offset: 192, + ), + ( + name: Some("num_planes"), + ty: 1, + binding: None, + offset: 200, + ), + ], + span: 208, + ), + ), + ( + name: None, + inner: Image( + dim: D2, + arrayed: false, + class: External, + ), + ), + ( + name: None, + inner: Sampler( + comparison: false, + ), + ), + ( + name: None, + inner: Vector( + size: Quad, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ], + special_types: ( + ray_desc: None, + ray_intersection: None, + ray_vertex_return: None, + external_texture_params: Some(7), + external_texture_transfer_function: Some(6), + predeclared_types: {}, + ), + constants: [], + overrides: [], + global_variables: [ + ( + name: Some("tex"), + space: Handle, + binding: Some(( + group: 0, + binding: 0, + )), + ty: 8, + init: None, + ), + ( + name: Some("samp"), + space: Handle, + binding: Some(( + group: 0, + binding: 1, + )), + ty: 9, + init: None, + ), + ], + global_expressions: [], + functions: [ + ( + name: Some("test"), + arguments: [ + ( + name: Some("t"), + ty: 8, + binding: None, + ), + ], + result: Some(( + ty: 10, + binding: None, + )), + local_variables: [ + ( + name: Some("a"), + ty: 10, + init: None, + ), + ( + name: Some("b"), + ty: 10, + init: None, + ), + ( + name: Some("c"), + ty: 10, + init: None, + ), + ( + name: Some("d"), + ty: 2, + init: None, + ), + ], + expressions: [ + FunctionArgument(0), + GlobalVariable(1), + Literal(F32(0.0)), + Splat( + size: Bi, + value: 2, + ), + ImageSample( + image: 0, + sampler: 1, + gather: None, + coordinate: 3, + array_index: None, + offset: None, + level: Zero, + depth_ref: None, + clamp_to_edge: true, + ), + LocalVariable(0), + Literal(I32(0)), + Splat( + size: Bi, + value: 6, + ), + ImageLoad( + image: 0, + coordinate: 7, + array_index: None, + sample: None, + level: None, + ), + LocalVariable(1), + Literal(U32(0)), + Splat( + size: Bi, + value: 10, + ), + ImageLoad( + image: 0, + coordinate: 11, + array_index: None, + sample: None, + level: None, + ), + LocalVariable(2), + ImageQuery( + image: 0, + query: Size( + level: None, + ), + ), + LocalVariable(3), + Load( + pointer: 5, + ), + Load( + pointer: 9, + ), + Binary( + op: Add, + left: 16, + right: 17, + ), + Load( + pointer: 13, + ), + Binary( + op: Add, + left: 18, + right: 19, + ), + Load( + pointer: 15, + ), + As( + expr: 21, + kind: Float, + convert: Some(4), + ), + Swizzle( + size: Quad, + vector: 22, + pattern: (X, Y, X, Y), + ), + Binary( + op: Add, + left: 20, + right: 23, + ), + ], + named_expressions: { + 0: "t", + }, + body: [ + Emit(( + start: 3, + end: 5, + )), + Store( + pointer: 5, + value: 4, + ), + Emit(( + start: 7, + end: 9, + )), + Store( + pointer: 9, + value: 8, + ), + Emit(( + start: 11, + end: 13, + )), + Store( + pointer: 13, + value: 12, + ), + Emit(( + start: 14, + end: 15, + )), + Store( + pointer: 15, + value: 14, + ), + Emit(( + start: 16, + end: 25, + )), + Return( + value: Some(24), + ), + ], + diagnostic_filter_leaf: None, + ), + ], + entry_points: [ + ( + name: "fragment_main", + stage: Fragment, + early_depth_test: None, + workgroup_size: (0, 0, 0), + workgroup_size_overrides: None, + function: ( + name: Some("fragment_main"), + arguments: [], + result: Some(( + ty: 10, + binding: Some(Location( + location: 0, + interpolation: Some(Perspective), + sampling: Some(Center), + blend_src: None, + )), + )), + local_variables: [], + expressions: [ + GlobalVariable(0), + CallResult(0), + ], + named_expressions: {}, + body: [ + Call( + function: 0, + arguments: [ + 0, + ], + result: Some(1), + ), + Return( + value: Some(1), + ), + ], + diagnostic_filter_leaf: None, + ), + ), + ( + name: "vertex_main", + stage: Vertex, + early_depth_test: None, + workgroup_size: (0, 0, 0), + workgroup_size_overrides: None, + function: ( + name: Some("vertex_main"), + arguments: [], + result: Some(( + ty: 10, + binding: Some(BuiltIn(Position( + invariant: false, + ))), + )), + local_variables: [], + expressions: [ + GlobalVariable(0), + CallResult(0), + ], + named_expressions: {}, + body: [ + Call( + function: 0, + arguments: [ + 0, + ], + result: Some(1), + ), + Return( + value: Some(1), + ), + ], + diagnostic_filter_leaf: None, + ), + ), + ( + name: "compute_main", + stage: Compute, + early_depth_test: None, + workgroup_size: (1, 1, 1), + workgroup_size_overrides: None, + function: ( + name: Some("compute_main"), + arguments: [], + result: None, + local_variables: [], + expressions: [ + GlobalVariable(0), + CallResult(0), + ], + named_expressions: {}, + body: [ + Call( + function: 0, + arguments: [ + 0, + ], + result: Some(1), + ), + Return( + value: None, + ), + ], + diagnostic_filter_leaf: None, + ), + ), + ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, + doc_comments: None, +) \ No newline at end of file diff --git a/naga/tests/out/ir/wgsl-texture-external.ron b/naga/tests/out/ir/wgsl-texture-external.ron new file mode 100644 index 00000000000..2b9e1c8d5e4 --- /dev/null +++ b/naga/tests/out/ir/wgsl-texture-external.ron @@ -0,0 +1,491 @@ +( + types: [ + ( + name: None, + inner: Scalar(( + kind: Float, + width: 4, + )), + ), + ( + name: None, + inner: Scalar(( + kind: Uint, + width: 4, + )), + ), + ( + name: None, + inner: Vector( + size: Bi, + scalar: ( + kind: Uint, + width: 4, + ), + ), + ), + ( + name: None, + inner: Matrix( + columns: Tri, + rows: Bi, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ( + name: None, + inner: Matrix( + columns: Tri, + rows: Tri, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ( + name: None, + inner: Matrix( + columns: Quad, + rows: Quad, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ( + name: Some("NagaExternalTextureTransferFn"), + inner: Struct( + members: [ + ( + name: Some("a"), + ty: 0, + binding: None, + offset: 0, + ), + ( + name: Some("b"), + ty: 0, + binding: None, + offset: 4, + ), + ( + name: Some("g"), + ty: 0, + binding: None, + offset: 8, + ), + ( + name: Some("k"), + ty: 0, + binding: None, + offset: 12, + ), + ], + span: 16, + ), + ), + ( + name: Some("NagaExternalTextureParams"), + inner: Struct( + members: [ + ( + name: Some("yuv_conversion_matrix"), + ty: 5, + binding: None, + offset: 0, + ), + ( + name: Some("gamut_conversion_matrix"), + ty: 4, + binding: None, + offset: 64, + ), + ( + name: Some("src_tf"), + ty: 6, + binding: None, + offset: 112, + ), + ( + name: Some("dst_tf"), + ty: 6, + binding: None, + offset: 128, + ), + ( + name: Some("sample_transform"), + ty: 3, + binding: None, + offset: 144, + ), + ( + name: Some("load_transform"), + ty: 3, + binding: None, + offset: 168, + ), + ( + name: Some("size"), + ty: 2, + binding: None, + offset: 192, + ), + ( + name: Some("num_planes"), + ty: 1, + binding: None, + offset: 200, + ), + ], + span: 208, + ), + ), + ( + name: None, + inner: Image( + dim: D2, + arrayed: false, + class: External, + ), + ), + ( + name: None, + inner: Sampler( + comparison: false, + ), + ), + ( + name: None, + inner: Vector( + size: Quad, + scalar: ( + kind: Float, + width: 4, + ), + ), + ), + ], + special_types: ( + ray_desc: None, + ray_intersection: None, + ray_vertex_return: None, + external_texture_params: Some(7), + external_texture_transfer_function: Some(6), + predeclared_types: {}, + ), + constants: [], + overrides: [], + global_variables: [ + ( + name: Some("tex"), + space: Handle, + binding: Some(( + group: 0, + binding: 0, + )), + ty: 8, + init: None, + ), + ( + name: Some("samp"), + space: Handle, + binding: Some(( + group: 0, + binding: 1, + )), + ty: 9, + init: None, + ), + ], + global_expressions: [], + functions: [ + ( + name: Some("test"), + arguments: [ + ( + name: Some("t"), + ty: 8, + binding: None, + ), + ], + result: Some(( + ty: 10, + binding: None, + )), + local_variables: [ + ( + name: Some("a"), + ty: 10, + init: None, + ), + ( + name: Some("b"), + ty: 10, + init: None, + ), + ( + name: Some("c"), + ty: 10, + init: None, + ), + ( + name: Some("d"), + ty: 2, + init: None, + ), + ], + expressions: [ + FunctionArgument(0), + GlobalVariable(1), + Literal(F32(0.0)), + Splat( + size: Bi, + value: 2, + ), + ImageSample( + image: 0, + sampler: 1, + gather: None, + coordinate: 3, + array_index: None, + offset: None, + level: Zero, + depth_ref: None, + clamp_to_edge: true, + ), + LocalVariable(0), + Literal(I32(0)), + Splat( + size: Bi, + value: 6, + ), + ImageLoad( + image: 0, + coordinate: 7, + array_index: None, + sample: None, + level: None, + ), + LocalVariable(1), + Literal(U32(0)), + Splat( + size: Bi, + value: 10, + ), + ImageLoad( + image: 0, + coordinate: 11, + array_index: None, + sample: None, + level: None, + ), + LocalVariable(2), + ImageQuery( + image: 0, + query: Size( + level: None, + ), + ), + LocalVariable(3), + Load( + pointer: 5, + ), + Load( + pointer: 9, + ), + Binary( + op: Add, + left: 16, + right: 17, + ), + Load( + pointer: 13, + ), + Binary( + op: Add, + left: 18, + right: 19, + ), + Load( + pointer: 15, + ), + As( + expr: 21, + kind: Float, + convert: Some(4), + ), + Swizzle( + size: Quad, + vector: 22, + pattern: (X, Y, X, Y), + ), + Binary( + op: Add, + left: 20, + right: 23, + ), + ], + named_expressions: { + 0: "t", + }, + body: [ + Emit(( + start: 3, + end: 5, + )), + Store( + pointer: 5, + value: 4, + ), + Emit(( + start: 7, + end: 9, + )), + Store( + pointer: 9, + value: 8, + ), + Emit(( + start: 11, + end: 13, + )), + Store( + pointer: 13, + value: 12, + ), + Emit(( + start: 14, + end: 15, + )), + Store( + pointer: 15, + value: 14, + ), + Emit(( + start: 16, + end: 25, + )), + Return( + value: Some(24), + ), + ], + diagnostic_filter_leaf: None, + ), + ], + entry_points: [ + ( + name: "fragment_main", + stage: Fragment, + early_depth_test: None, + workgroup_size: (0, 0, 0), + workgroup_size_overrides: None, + function: ( + name: Some("fragment_main"), + arguments: [], + result: Some(( + ty: 10, + binding: Some(Location( + location: 0, + interpolation: Some(Perspective), + sampling: Some(Center), + blend_src: None, + )), + )), + local_variables: [], + expressions: [ + GlobalVariable(0), + CallResult(0), + ], + named_expressions: {}, + body: [ + Call( + function: 0, + arguments: [ + 0, + ], + result: Some(1), + ), + Return( + value: Some(1), + ), + ], + diagnostic_filter_leaf: None, + ), + ), + ( + name: "vertex_main", + stage: Vertex, + early_depth_test: None, + workgroup_size: (0, 0, 0), + workgroup_size_overrides: None, + function: ( + name: Some("vertex_main"), + arguments: [], + result: Some(( + ty: 10, + binding: Some(BuiltIn(Position( + invariant: false, + ))), + )), + local_variables: [], + expressions: [ + GlobalVariable(0), + CallResult(0), + ], + named_expressions: {}, + body: [ + Call( + function: 0, + arguments: [ + 0, + ], + result: Some(1), + ), + Return( + value: Some(1), + ), + ], + diagnostic_filter_leaf: None, + ), + ), + ( + name: "compute_main", + stage: Compute, + early_depth_test: None, + workgroup_size: (1, 1, 1), + workgroup_size_overrides: None, + function: ( + name: Some("compute_main"), + arguments: [], + result: None, + local_variables: [], + expressions: [ + GlobalVariable(0), + CallResult(0), + ], + named_expressions: {}, + body: [ + Call( + function: 0, + arguments: [ + 0, + ], + result: Some(1), + ), + Return( + value: None, + ), + ], + diagnostic_filter_leaf: None, + ), + ), + ], + diagnostic_filters: [], + diagnostic_filter_leaf: None, + doc_comments: None, +) \ No newline at end of file diff --git a/naga/tests/out/ir/wgsl-types_with_comments.compact.ron b/naga/tests/out/ir/wgsl-types_with_comments.compact.ron index dde1bd367b0..7186209f00e 100644 --- a/naga/tests/out/ir/wgsl-types_with_comments.compact.ron +++ b/naga/tests/out/ir/wgsl-types_with_comments.compact.ron @@ -37,6 +37,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/ir/wgsl-types_with_comments.ron b/naga/tests/out/ir/wgsl-types_with_comments.ron index 3a23c49c4f2..480b0d2337f 100644 --- a/naga/tests/out/ir/wgsl-types_with_comments.ron +++ b/naga/tests/out/ir/wgsl-types_with_comments.ron @@ -62,6 +62,8 @@ ray_desc: None, ray_intersection: None, ray_vertex_return: None, + external_texture_params: None, + external_texture_transfer_function: None, predeclared_types: {}, ), constants: [ diff --git a/naga/tests/out/msl/spv-do-while.msl b/naga/tests/out/msl/spv-do-while.msl index 7e5d111b505..d58be5ef508 100644 --- a/naga/tests/out/msl/spv-do-while.msl +++ b/naga/tests/out/msl/spv-do-while.msl @@ -5,7 +5,7 @@ using metal::uint; -void fb1_( +void f_u0028_b1_u003b( thread bool& cond ) { uint2 loop_bound = uint2(4294967295u); @@ -29,7 +29,7 @@ void main_1( ) { bool param = {}; param = false; - fb1_(param); + f_u0028_b1_u003b(param); return; } diff --git a/naga/tests/out/msl/spv-fetch_depth.msl b/naga/tests/out/msl/spv-fetch_depth.msl index 867a284372c..ce239c4f925 100644 --- a/naga/tests/out/msl/spv-fetch_depth.msl +++ b/naga/tests/out/msl/spv-fetch_depth.msl @@ -22,7 +22,7 @@ void function( return; } -kernel void cullfetch_depth( +kernel void cull_fetch_depth( device type_2& global [[user(fake0)]] , device type_4 const& global_1 [[user(fake0)]] , metal::depth2d global_2 [[user(fake0)]] diff --git a/naga/tests/out/msl/wgsl-7995-unicode-idents.msl b/naga/tests/out/msl/wgsl-7995-unicode-idents.msl new file mode 100644 index 00000000000..9f912706d1c --- /dev/null +++ b/naga/tests/out/msl/wgsl-7995-unicode-idents.msl @@ -0,0 +1,21 @@ +// language: metal1.0 +#include +#include + +using metal::uint; + + +float compute( + device float const& asdf +) { + float _e1 = asdf; + float u03b8_2_ = _e1 + 9001.0; + return u03b8_2_; +} + +kernel void main_( + device float const& asdf [[user(fake0)]] +) { + float _e0 = compute(asdf); + return; +} diff --git a/naga/tests/out/msl/wgsl-atomicCompareExchange.msl b/naga/tests/out/msl/wgsl-atomicCompareExchange.msl index 72c418b9ed3..967e003a791 100644 --- a/naga/tests/out/msl/wgsl-atomicCompareExchange.msl +++ b/naga/tests/out/msl/wgsl-atomicCompareExchange.msl @@ -10,19 +10,19 @@ struct type_2 { struct type_4 { metal::atomic_uint inner[128]; }; -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; char _pad2[3]; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; char _pad2[3]; }; template -_atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit( device A *atomic_ptr, int cmp, int v @@ -31,10 +31,10 @@ _atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultSint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Sint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit( threadgroup A *atomic_ptr, int cmp, int v @@ -43,11 +43,11 @@ _atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultSint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Sint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit( device A *atomic_ptr, uint cmp, uint v @@ -56,10 +56,10 @@ _atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultUint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Uint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit( threadgroup A *atomic_ptr, uint cmp, uint v @@ -68,7 +68,7 @@ _atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultUint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Uint_4_{cmp, swapped}; } constant uint SIZE = 128u; @@ -112,7 +112,7 @@ kernel void test_atomic_compare_exchange_i32_( int new_ = as_type(as_type(_e14) + 1.0); uint _e20 = i; int _e22 = old; - _atomic_compare_exchange_resultSint4_ _e23 = naga_atomic_compare_exchange_weak_explicit(&arr_i32_.inner[_e20], _e22, new_); + _atomic_compare_exchange_result_Sint_4_ _e23 = naga_atomic_compare_exchange_weak_explicit(&arr_i32_.inner[_e20], _e22, new_); old = _e23.old_value; exchanged = _e23.exchanged; } @@ -163,7 +163,7 @@ kernel void test_atomic_compare_exchange_u32_( uint new_1 = as_type(as_type(_e14) + 1.0); uint _e20 = i_1; uint _e22 = old_1; - _atomic_compare_exchange_resultUint4_ _e23 = naga_atomic_compare_exchange_weak_explicit(&arr_u32_.inner[_e20], _e22, new_1); + _atomic_compare_exchange_result_Uint_4_ _e23 = naga_atomic_compare_exchange_weak_explicit(&arr_u32_.inner[_e20], _e22, new_1); old_1 = _e23.old_value; exchanged_1 = _e23.exchanged; } diff --git a/naga/tests/out/msl/wgsl-atomicOps.msl b/naga/tests/out/msl/wgsl-atomicOps.msl index 63cab2a80f5..4afdde1540f 100644 --- a/naga/tests/out/msl/wgsl-atomicOps.msl +++ b/naga/tests/out/msl/wgsl-atomicOps.msl @@ -11,19 +11,19 @@ struct Struct { metal::atomic_uint atomic_scalar; type_2 atomic_arr; }; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; char _pad2[3]; }; -struct _atomic_compare_exchange_resultSint4_ { +struct _atomic_compare_exchange_result_Sint_4_ { int old_value; bool exchanged; char _pad2[3]; }; template -_atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit( device A *atomic_ptr, uint cmp, uint v @@ -32,10 +32,10 @@ _atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultUint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Uint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit( threadgroup A *atomic_ptr, uint cmp, uint v @@ -44,11 +44,11 @@ _atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultUint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Uint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit( device A *atomic_ptr, int cmp, int v @@ -57,10 +57,10 @@ _atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultSint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Sint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Sint_4_ naga_atomic_compare_exchange_weak_explicit( threadgroup A *atomic_ptr, int cmp, int v @@ -69,7 +69,7 @@ _atomic_compare_exchange_resultSint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultSint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Sint_4_{cmp, swapped}; } struct cs_mainInput { @@ -182,13 +182,13 @@ kernel void cs_main( int _e295 = metal::atomic_exchange_explicit(&workgroup_atomic_arr.inner[1], 1, metal::memory_order_relaxed); uint _e299 = metal::atomic_exchange_explicit(&workgroup_struct.atomic_scalar, 1u, metal::memory_order_relaxed); int _e304 = metal::atomic_exchange_explicit(&workgroup_struct.atomic_arr.inner[1], 1, metal::memory_order_relaxed); - _atomic_compare_exchange_resultUint4_ _e308 = naga_atomic_compare_exchange_weak_explicit(&storage_atomic_scalar, 1u, 2u); - _atomic_compare_exchange_resultSint4_ _e313 = naga_atomic_compare_exchange_weak_explicit(&storage_atomic_arr.inner[1], 1, 2); - _atomic_compare_exchange_resultUint4_ _e318 = naga_atomic_compare_exchange_weak_explicit(&storage_struct.atomic_scalar, 1u, 2u); - _atomic_compare_exchange_resultSint4_ _e324 = naga_atomic_compare_exchange_weak_explicit(&storage_struct.atomic_arr.inner[1], 1, 2); - _atomic_compare_exchange_resultUint4_ _e328 = naga_atomic_compare_exchange_weak_explicit(&workgroup_atomic_scalar, 1u, 2u); - _atomic_compare_exchange_resultSint4_ _e333 = naga_atomic_compare_exchange_weak_explicit(&workgroup_atomic_arr.inner[1], 1, 2); - _atomic_compare_exchange_resultUint4_ _e338 = naga_atomic_compare_exchange_weak_explicit(&workgroup_struct.atomic_scalar, 1u, 2u); - _atomic_compare_exchange_resultSint4_ _e344 = naga_atomic_compare_exchange_weak_explicit(&workgroup_struct.atomic_arr.inner[1], 1, 2); + _atomic_compare_exchange_result_Uint_4_ _e308 = naga_atomic_compare_exchange_weak_explicit(&storage_atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Sint_4_ _e313 = naga_atomic_compare_exchange_weak_explicit(&storage_atomic_arr.inner[1], 1, 2); + _atomic_compare_exchange_result_Uint_4_ _e318 = naga_atomic_compare_exchange_weak_explicit(&storage_struct.atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Sint_4_ _e324 = naga_atomic_compare_exchange_weak_explicit(&storage_struct.atomic_arr.inner[1], 1, 2); + _atomic_compare_exchange_result_Uint_4_ _e328 = naga_atomic_compare_exchange_weak_explicit(&workgroup_atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Sint_4_ _e333 = naga_atomic_compare_exchange_weak_explicit(&workgroup_atomic_arr.inner[1], 1, 2); + _atomic_compare_exchange_result_Uint_4_ _e338 = naga_atomic_compare_exchange_weak_explicit(&workgroup_struct.atomic_scalar, 1u, 2u); + _atomic_compare_exchange_result_Sint_4_ _e344 = naga_atomic_compare_exchange_weak_explicit(&workgroup_struct.atomic_arr.inner[1], 1, 2); return; } diff --git a/naga/tests/out/msl/wgsl-control-flow.msl b/naga/tests/out/msl/wgsl-control-flow.msl index b93f9584654..ccb36a32edd 100644 --- a/naga/tests/out/msl/wgsl-control-flow.msl +++ b/naga/tests/out/msl/wgsl-control-flow.msl @@ -58,19 +58,44 @@ void control_flow( } case 2: { pos = 1; - return; + break; } case 3: { pos = 2; + break; + } + case 4: { + break; + } + default: { + pos = 3; + break; + } + } + int _e15 = pos; + switch(_e15) { + case 1: { + pos = 0; return; } + case 2: { + pos = 1; + return; + } + case 3: case 4: { + pos = 2; return; } - default: { + case 5: + case 6: { pos = 3; return; } + default: { + pos = 4; + return; + } } } diff --git a/naga/tests/out/msl/wgsl-empty-if.msl b/naga/tests/out/msl/wgsl-empty-if.msl new file mode 100644 index 00000000000..d9ce803d617 --- /dev/null +++ b/naga/tests/out/msl/wgsl-empty-if.msl @@ -0,0 +1,16 @@ +// language: metal1.0 +#include +#include + +using metal::uint; + + +struct compInput { +}; +kernel void comp( + metal::uint3 id [[thread_position_in_grid]] +) { + if (id.x == 0u) { + } + return; +} diff --git a/naga/tests/out/msl/wgsl-msl-vpt-formats-x1.msl b/naga/tests/out/msl/wgsl-msl-vpt-formats-x1.msl index b2a461c0b5e..766ffff6e59 100644 --- a/naga/tests/out/msl/wgsl-msl-vpt-formats-x1.msl +++ b/naga/tests/out/msl/wgsl-msl-vpt-formats-x1.msl @@ -120,11 +120,11 @@ metal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort float unpackSnorm16_(metal::ushort b0, metal::ushort b1) { return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x; } -metal::float2 unpackSnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) { - return metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2); +metal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) { + return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0); } -metal::float4 unpackSnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) { - return metal::float4(metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2), metal::unpack_snorm2x16_to_float(b5 << 24 | b4 << 16 | b7 << 8 | b6)); +metal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) { + return metal::float4(metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0), metal::unpack_snorm2x16_to_float(b7 << 24 | b6 << 16 | b5 << 8 | b4)); } float unpackFloat16_(metal::ushort b0, metal::ushort b1) { return float(as_type(metal::ushort(b1 << 8 | b0))); diff --git a/naga/tests/out/msl/wgsl-msl-vpt-formats-x2.msl b/naga/tests/out/msl/wgsl-msl-vpt-formats-x2.msl index 46d1985d329..0559be74a89 100644 --- a/naga/tests/out/msl/wgsl-msl-vpt-formats-x2.msl +++ b/naga/tests/out/msl/wgsl-msl-vpt-formats-x2.msl @@ -120,11 +120,11 @@ metal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort float unpackSnorm16_(metal::ushort b0, metal::ushort b1) { return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x; } -metal::float2 unpackSnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) { - return metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2); +metal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) { + return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0); } -metal::float4 unpackSnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) { - return metal::float4(metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2), metal::unpack_snorm2x16_to_float(b5 << 24 | b4 << 16 | b7 << 8 | b6)); +metal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) { + return metal::float4(metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0), metal::unpack_snorm2x16_to_float(b7 << 24 | b6 << 16 | b5 << 8 | b4)); } float unpackFloat16_(metal::ushort b0, metal::ushort b1) { return float(as_type(metal::ushort(b1 << 8 | b0))); diff --git a/naga/tests/out/msl/wgsl-msl-vpt-formats-x3.msl b/naga/tests/out/msl/wgsl-msl-vpt-formats-x3.msl index bd7471b3818..7db6980631d 100644 --- a/naga/tests/out/msl/wgsl-msl-vpt-formats-x3.msl +++ b/naga/tests/out/msl/wgsl-msl-vpt-formats-x3.msl @@ -120,11 +120,11 @@ metal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort float unpackSnorm16_(metal::ushort b0, metal::ushort b1) { return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x; } -metal::float2 unpackSnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) { - return metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2); +metal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) { + return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0); } -metal::float4 unpackSnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) { - return metal::float4(metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2), metal::unpack_snorm2x16_to_float(b5 << 24 | b4 << 16 | b7 << 8 | b6)); +metal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) { + return metal::float4(metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0), metal::unpack_snorm2x16_to_float(b7 << 24 | b6 << 16 | b5 << 8 | b4)); } float unpackFloat16_(metal::ushort b0, metal::ushort b1) { return float(as_type(metal::ushort(b1 << 8 | b0))); diff --git a/naga/tests/out/msl/wgsl-msl-vpt-formats-x4.msl b/naga/tests/out/msl/wgsl-msl-vpt-formats-x4.msl index dba6657d6d5..d35045ffc75 100644 --- a/naga/tests/out/msl/wgsl-msl-vpt-formats-x4.msl +++ b/naga/tests/out/msl/wgsl-msl-vpt-formats-x4.msl @@ -120,11 +120,11 @@ metal::float4 unpackUnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort float unpackSnorm16_(metal::ushort b0, metal::ushort b1) { return metal::unpack_snorm2x16_to_float(b1 << 8 | b0).x; } -metal::float2 unpackSnorm16x2_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3) { - return metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2); +metal::float2 unpackSnorm16x2_(uint b0, uint b1, uint b2, uint b3) { + return metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0); } -metal::float4 unpackSnorm16x4_(metal::ushort b0, metal::ushort b1, metal::ushort b2, metal::ushort b3, metal::ushort b4, metal::ushort b5, metal::ushort b6, metal::ushort b7) { - return metal::float4(metal::unpack_snorm2x16_to_float(b1 << 24 | b0 << 16 | b3 << 8 | b2), metal::unpack_snorm2x16_to_float(b5 << 24 | b4 << 16 | b7 << 8 | b6)); +metal::float4 unpackSnorm16x4_(uint b0, uint b1, uint b2, uint b3, uint b4, uint b5, uint b6, uint b7) { + return metal::float4(metal::unpack_snorm2x16_to_float(b3 << 24 | b2 << 16 | b1 << 8 | b0), metal::unpack_snorm2x16_to_float(b7 << 24 | b6 << 16 | b5 << 8 | b4)); } float unpackFloat16_(metal::ushort b0, metal::ushort b1) { return float(as_type(metal::ushort(b1 << 8 | b0))); diff --git a/naga/tests/out/msl/wgsl-overrides-atomicCompareExchangeWeak.msl b/naga/tests/out/msl/wgsl-overrides-atomicCompareExchangeWeak.msl index 48f09ee6de2..42b93fdfcea 100644 --- a/naga/tests/out/msl/wgsl-overrides-atomicCompareExchangeWeak.msl +++ b/naga/tests/out/msl/wgsl-overrides-atomicCompareExchangeWeak.msl @@ -4,14 +4,14 @@ using metal::uint; -struct _atomic_compare_exchange_resultUint4_ { +struct _atomic_compare_exchange_result_Uint_4_ { uint old_value; bool exchanged; char _pad2[3]; }; template -_atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit( device A *atomic_ptr, uint cmp, uint v @@ -20,10 +20,10 @@ _atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultUint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Uint_4_{cmp, swapped}; } template -_atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit( +_atomic_compare_exchange_result_Uint_4_ naga_atomic_compare_exchange_weak_explicit( threadgroup A *atomic_ptr, uint cmp, uint v @@ -32,7 +32,7 @@ _atomic_compare_exchange_resultUint4_ naga_atomic_compare_exchange_weak_explicit atomic_ptr, &cmp, v, metal::memory_order_relaxed, metal::memory_order_relaxed ); - return _atomic_compare_exchange_resultUint4_{cmp, swapped}; + return _atomic_compare_exchange_result_Uint_4_{cmp, swapped}; } constant int o = 2; @@ -44,6 +44,6 @@ kernel void f( metal::atomic_store_explicit(&a, 0, metal::memory_order_relaxed); } metal::threadgroup_barrier(metal::mem_flags::mem_threadgroup); - _atomic_compare_exchange_resultUint4_ _e5 = naga_atomic_compare_exchange_weak_explicit(&a, 2u, 1u); + _atomic_compare_exchange_result_Uint_4_ _e5 = naga_atomic_compare_exchange_weak_explicit(&a, 2u, 1u); return; } diff --git a/naga/tests/out/msl/wgsl-texture-external.msl b/naga/tests/out/msl/wgsl-texture-external.msl new file mode 100644 index 00000000000..3dade424fb3 --- /dev/null +++ b/naga/tests/out/msl/wgsl-texture-external.msl @@ -0,0 +1,190 @@ +// language: metal1.0 +#include +#include + +using metal::uint; + +struct NagaExternalTextureTransferFn { + float a; + float b; + float g; + float k; +}; +struct NagaExternalTextureParams { + metal::float4x4 yuv_conversion_matrix; + metal::float3x3 gamut_conversion_matrix; + NagaExternalTextureTransferFn src_tf; + NagaExternalTextureTransferFn dst_tf; + metal::float3x2 sample_transform; + metal::float3x2 load_transform; + metal::uint2 size; + uint num_planes; + char _pad8[4]; +}; +struct NagaExternalTextureWrapper { + metal::texture2d plane0; + metal::texture2d plane1; + metal::texture2d plane2; + NagaExternalTextureParams params; +}; + +float4 nagaTextureSampleBaseClampToEdge(NagaExternalTextureWrapper tex, metal::sampler samp, float2 coords) { + uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height()); + coords = tex.params.sample_transform * float3(coords, 1.0); + float2 bounds_min = tex.params.sample_transform * float3(0.0, 0.0, 1.0); + float2 bounds_max = tex.params.sample_transform * float3(1.0, 1.0, 1.0); + float4 bounds = float4(metal::min(bounds_min, bounds_max), metal::max(bounds_min, bounds_max)); + float2 plane0_half_texel = float2(0.5, 0.5) / float2(plane0_size); + float2 plane0_coords = metal::clamp(coords, bounds.xy + plane0_half_texel, bounds.zw - plane0_half_texel); + if (tex.params.num_planes == 1u) { + return tex.plane0.sample(samp, plane0_coords, metal::level(0.0f)); + } else { + uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height()); + float2 plane1_half_texel = float2(0.5, 0.5) / float2(plane1_size); + float2 plane1_coords = metal::clamp(coords, bounds.xy + plane1_half_texel, bounds.zw - plane1_half_texel); + float y = tex.plane0.sample(samp, plane0_coords, metal::level(0.0f)).r; + float2 uv = float2(0.0, 0.0); + if (tex.params.num_planes == 2u) { + uv = tex.plane1.sample(samp, plane1_coords, metal::level(0.0f)).xy; + } else { + uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height()); + float2 plane2_half_texel = float2(0.5, 0.5) / float2(plane2_size); + float2 plane2_coords = metal::clamp(coords, bounds.xy + plane2_half_texel, bounds.zw - plane1_half_texel); + uv.x = tex.plane1.sample(samp, plane1_coords, metal::level(0.0f)).x; + uv.y = tex.plane2.sample(samp, plane2_coords, metal::level(0.0f)).x; + } + float3 srcGammaRgb = (tex.params.yuv_conversion_matrix * float4(y, uv, 1.0)).rgb; + float3 srcLinearRgb = metal::select( + metal::pow((srcGammaRgb + tex.params.src_tf.a - 1.0) / tex.params.src_tf.a, tex.params.src_tf.g), + srcGammaRgb / tex.params.src_tf.k, + srcGammaRgb < tex.params.src_tf.k * tex.params.src_tf.b); + float3 dstLinearRgb = tex.params.gamut_conversion_matrix * srcLinearRgb; + float3 dstGammaRgb = metal::select( + tex.params.dst_tf.a * metal::pow(dstLinearRgb, 1.0 / tex.params.dst_tf.g) - (tex.params.dst_tf.a - 1), + tex.params.dst_tf.k * dstLinearRgb, + dstLinearRgb < tex.params.dst_tf.b); + return float4(dstGammaRgb, 1.0); + } +} + +float4 nagaTextureLoadExternal(NagaExternalTextureWrapper tex, uint2 coords) { + uint2 plane0_size = uint2(tex.plane0.get_width(), tex.plane0.get_height()); + uint2 cropped_size = metal::any(tex.params.size != 0) ? tex.params.size : plane0_size; + coords = metal::min(coords, cropped_size - 1); + uint2 plane0_coords = uint2(metal::round(tex.params.load_transform * float3(float2(coords), 1.0))); + if (tex.params.num_planes == 1u) { + return tex.plane0.read(plane0_coords); + } else { + uint2 plane1_size = uint2(tex.plane1.get_width(), tex.plane1.get_height()); + uint2 plane1_coords = uint2(metal::floor(float2(plane0_coords) * float2(plane1_size) / float2(plane0_size))); + float y = tex.plane0.read(plane0_coords).x; + float2 uv; + if (tex.params.num_planes == 2u) { + uv = tex.plane1.read(plane1_coords).xy; + } else { + uint2 plane2_size = uint2(tex.plane2.get_width(), tex.plane2.get_height()); + uint2 plane2_coords = uint2(metal::floor(float2(plane0_coords) * float2(plane2_size) / float2(plane0_size))); + uv = float2(tex.plane1.read(plane1_coords).x, tex.plane2.read(plane2_coords).x); + } + float3 srcGammaRgb = (tex.params.yuv_conversion_matrix * float4(y, uv, 1.0)).rgb; + float3 srcLinearRgb = metal::select( + metal::pow((srcGammaRgb + tex.params.src_tf.a - 1.0) / tex.params.src_tf.a, tex.params.src_tf.g), + srcGammaRgb / tex.params.src_tf.k, + srcGammaRgb < tex.params.src_tf.k * tex.params.src_tf.b); + float3 dstLinearRgb = tex.params.gamut_conversion_matrix * srcLinearRgb; + float3 dstGammaRgb = metal::select( + tex.params.dst_tf.a * metal::pow(dstLinearRgb, 1.0 / tex.params.dst_tf.g) - (tex.params.dst_tf.a - 1), + tex.params.dst_tf.k * dstLinearRgb, + dstLinearRgb < tex.params.dst_tf.b); + return float4(dstGammaRgb, 1.0); + } +} + +uint2 nagaTextureDimensionsExternal(NagaExternalTextureWrapper tex) { + if (metal::any(tex.params.size != uint2(0u))) { + return tex.params.size; + } else { + return uint2(tex.plane0.get_width(), tex.plane0.get_height()); + } +} + +metal::float4 test( + NagaExternalTextureWrapper t, + metal::sampler samp +) { + metal::float4 a = {}; + metal::float4 b = {}; + metal::float4 c = {}; + metal::uint2 d = {}; + metal::float4 _e4 = nagaTextureSampleBaseClampToEdge(t, samp, metal::float2(0.0)); + a = _e4; + metal::float4 _e8 = nagaTextureLoadExternal(t, metal::uint2(metal::int2(0))); + b = _e8; + metal::float4 _e12 = nagaTextureLoadExternal(t, metal::uint2(metal::uint2(0u))); + c = _e12; + d = nagaTextureDimensionsExternal(t); + metal::float4 _e16 = a; + metal::float4 _e17 = b; + metal::float4 _e19 = c; + metal::uint2 _e21 = d; + return ((_e16 + _e17) + _e19) + static_cast(_e21).xyxy; +} + +struct fragment_mainOutput { + metal::float4 member [[color(0)]]; +}; +fragment fragment_mainOutput fragment_main( + metal::texture2d tex_plane0_ [[texture(0)]] +, metal::texture2d tex_plane1_ [[texture(1)]] +, metal::texture2d tex_plane2_ [[texture(2)]] +, constant NagaExternalTextureParams& tex_params [[buffer(0)]] +, metal::sampler samp [[sampler(0)]] +) { + const NagaExternalTextureWrapper tex { + .plane0 = tex_plane0_, + .plane1 = tex_plane1_, + .plane2 = tex_plane2_, + .params = tex_params, + }; + metal::float4 _e1 = test(tex, samp); + return fragment_mainOutput { _e1 }; +} + + +struct vertex_mainOutput { + metal::float4 member_1 [[position]]; +}; +vertex vertex_mainOutput vertex_main( + metal::texture2d tex_plane0_ [[texture(0)]] +, metal::texture2d tex_plane1_ [[texture(1)]] +, metal::texture2d tex_plane2_ [[texture(2)]] +, constant NagaExternalTextureParams& tex_params [[buffer(0)]] +, metal::sampler samp [[sampler(0)]] +) { + const NagaExternalTextureWrapper tex { + .plane0 = tex_plane0_, + .plane1 = tex_plane1_, + .plane2 = tex_plane2_, + .params = tex_params, + }; + metal::float4 _e1 = test(tex, samp); + return vertex_mainOutput { _e1 }; +} + + +kernel void compute_main( + metal::texture2d tex_plane0_ [[texture(0)]] +, metal::texture2d tex_plane1_ [[texture(1)]] +, metal::texture2d tex_plane2_ [[texture(2)]] +, constant NagaExternalTextureParams& tex_params [[buffer(0)]] +, metal::sampler samp [[sampler(0)]] +) { + const NagaExternalTextureWrapper tex { + .plane0 = tex_plane0_, + .plane1 = tex_plane1_, + .plane2 = tex_plane2_, + .params = tex_params, + }; + metal::float4 _e1 = test(tex, samp); + return; +} diff --git a/naga/tests/out/spv/wgsl-6438-conflicting-idents.spvasm b/naga/tests/out/spv/wgsl-6438-conflicting-idents.spvasm index 589eeb34624..be4ca450115 100644 --- a/naga/tests/out/spv/wgsl-6438-conflicting-idents.spvasm +++ b/naga/tests/out/spv/wgsl-6438-conflicting-idents.spvasm @@ -26,8 +26,8 @@ OpDecorate %32 Location 0 %14 = OpTypePointer Output %5 %13 = OpVariable %14 Output %16 = OpTypeFunction %2 -%17 = OpConstant %4 0.0 -%18 = OpConstant %4 1.0 +%17 = OpConstant %4 0 +%18 = OpConstant %4 1 %20 = OpTypePointer Function %6 %21 = OpConstantNull %6 %23 = OpTypePointer Function %3 diff --git a/naga/tests/out/spv/wgsl-7048-multiple-dynamic-1.spvasm b/naga/tests/out/spv/wgsl-7048-multiple-dynamic-1.spvasm index ad042682297..dd87117cfc4 100644 --- a/naga/tests/out/spv/wgsl-7048-multiple-dynamic-1.spvasm +++ b/naga/tests/out/spv/wgsl-7048-multiple-dynamic-1.spvasm @@ -18,7 +18,7 @@ OpDecorate %5 ArrayStride 16 %9 = OpTypeInt 32 1 %12 = OpTypeFunction %2 %13 = OpConstantNull %5 -%14 = OpConstant %4 0.0 +%14 = OpConstant %4 0 %15 = OpConstantComposite %8 %14 %14 %14 %14 %16 = OpConstant %9 0 %18 = OpTypePointer Function %8 diff --git a/naga/tests/out/spv/wgsl-7048-multiple-dynamic-2.spvasm b/naga/tests/out/spv/wgsl-7048-multiple-dynamic-2.spvasm index 504bd8f4eb3..1eba8a12a5e 100644 --- a/naga/tests/out/spv/wgsl-7048-multiple-dynamic-2.spvasm +++ b/naga/tests/out/spv/wgsl-7048-multiple-dynamic-2.spvasm @@ -20,7 +20,7 @@ OpDecorate %11 Location 0 %12 = OpTypePointer Output %3 %11 = OpVariable %12 Output %14 = OpTypeFunction %2 -%15 = OpConstant %4 0.0 +%15 = OpConstant %4 0 %16 = OpConstantComposite %5 %15 %15 %17 = OpConstantComposite %6 %16 %16 %18 = OpConstant %9 0 diff --git a/naga/tests/out/spv/wgsl-7048-multiple-dynamic-3.spvasm b/naga/tests/out/spv/wgsl-7048-multiple-dynamic-3.spvasm index d11c182d519..2e4ab1e1aef 100644 --- a/naga/tests/out/spv/wgsl-7048-multiple-dynamic-3.spvasm +++ b/naga/tests/out/spv/wgsl-7048-multiple-dynamic-3.spvasm @@ -20,7 +20,7 @@ OpDecorate %6 ArrayStride 16 %13 = OpTypeFunction %5 %6 %8 %14 = OpConstant %8 0 %15 = OpConstant %8 1 -%16 = OpConstant %3 0.0 +%16 = OpConstant %3 0 %17 = OpConstantComposite %4 %16 %16 %16 %18 = OpConstantComposite %5 %16 %17 %20 = OpTypePointer Function %8 diff --git a/naga/tests/out/spv/wgsl-7995-unicode-idents.spvasm b/naga/tests/out/spv/wgsl-7995-unicode-idents.spvasm new file mode 100644 index 00000000000..a051e436ab5 --- /dev/null +++ b/naga/tests/out/spv/wgsl-7995-unicode-idents.spvasm @@ -0,0 +1,43 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 24 +OpCapability Shader +OpExtension "SPV_KHR_storage_buffer_storage_class" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %19 "main" +OpExecutionMode %19 LocalSize 1 1 1 +OpDecorate %4 NonWritable +OpDecorate %4 DescriptorSet 0 +OpDecorate %4 Binding 0 +OpDecorate %5 Block +OpMemberDecorate %5 0 Offset 0 +%2 = OpTypeVoid +%3 = OpTypeFloat 32 +%5 = OpTypeStruct %3 +%6 = OpTypePointer StorageBuffer %5 +%4 = OpVariable %6 StorageBuffer +%9 = OpTypeFunction %3 +%10 = OpTypePointer StorageBuffer %3 +%12 = OpTypeInt 32 0 +%11 = OpConstant %12 0 +%14 = OpConstant %3 9001 +%20 = OpTypeFunction %2 +%8 = OpFunction %3 None %9 +%7 = OpLabel +%13 = OpAccessChain %10 %4 %11 +OpBranch %15 +%15 = OpLabel +%16 = OpLoad %3 %13 +%17 = OpFAdd %3 %16 %14 +OpReturnValue %17 +OpFunctionEnd +%19 = OpFunction %2 None %20 +%18 = OpLabel +%21 = OpAccessChain %10 %4 %11 +OpBranch %22 +%22 = OpLabel +%23 = OpFunctionCall %3 %8 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/spv/wgsl-abstract-types-builtins.spvasm b/naga/tests/out/spv/wgsl-abstract-types-builtins.spvasm index 83ef259cba5..0601a126b1b 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-builtins.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-builtins.spvasm @@ -12,7 +12,7 @@ OpExecutionMode %6 LocalSize 1 1 1 %4 = OpTypeFloat 32 %7 = OpTypeFunction %2 %8 = OpConstant %3 1 -%9 = OpConstant %4 1.0 +%9 = OpConstant %4 1 %11 = OpTypePointer Function %3 %13 = OpTypePointer Function %4 %6 = OpFunction %2 None %7 diff --git a/naga/tests/out/spv/wgsl-abstract-types-const.spvasm b/naga/tests/out/spv/wgsl-abstract-types-const.spvasm index b1da55273f5..b1dd0dc374e 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-const.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-const.spvasm @@ -32,11 +32,11 @@ OpMemberDecorate %14 2 Offset 8 %19 = OpConstant %5 44 %20 = OpConstant %5 45 %21 = OpConstantComposite %6 %19 %20 -%22 = OpConstant %7 46.0 -%23 = OpConstant %7 47.0 +%22 = OpConstant %7 46 +%23 = OpConstant %7 47 %24 = OpConstantComposite %8 %22 %23 -%25 = OpConstant %7 48.0 -%26 = OpConstant %7 49.0 +%25 = OpConstant %7 48 +%26 = OpConstant %7 49 %27 = OpConstantComposite %8 %25 %26 %28 = OpConstant %5 42 %29 = OpConstant %5 43 @@ -45,14 +45,14 @@ OpMemberDecorate %14 2 Offset 8 %32 = OpConstantComposite %4 %31 %31 %33 = OpConstant %5 0 %34 = OpConstantComposite %6 %33 %33 -%35 = OpConstant %7 0.0 +%35 = OpConstant %7 0 %36 = OpConstantComposite %8 %35 %35 %37 = OpConstantComposite %9 %36 %36 -%38 = OpConstant %7 1.0 -%39 = OpConstant %7 2.0 +%38 = OpConstant %7 1 +%39 = OpConstant %7 2 %40 = OpConstantComposite %8 %38 %39 -%41 = OpConstant %7 3.0 -%42 = OpConstant %7 4.0 +%41 = OpConstant %7 3 +%42 = OpConstant %7 4 %43 = OpConstantComposite %8 %41 %42 %44 = OpConstantComposite %9 %40 %43 %45 = OpConstant %3 1 diff --git a/naga/tests/out/spv/wgsl-abstract-types-function-calls.spvasm b/naga/tests/out/spv/wgsl-abstract-types-function-calls.spvasm index 81b0a5e44c5..40931665253 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-function-calls.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-function-calls.spvasm @@ -34,7 +34,7 @@ OpDecorate %13 ArrayStride 4 %62 = OpTypeFunction %2 %13 %68 = OpTypeFunction %2 %3 %4 %72 = OpTypeFunction %2 -%73 = OpConstant %3 0.0 +%73 = OpConstant %3 0 %74 = OpConstant %4 0 %75 = OpConstant %5 0 %76 = OpConstantComposite %6 %73 %73 diff --git a/naga/tests/out/spv/wgsl-abstract-types-let.spvasm b/naga/tests/out/spv/wgsl-abstract-types-let.spvasm index f0b7ea3c4f3..c9394ebaf4c 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-let.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-let.spvasm @@ -34,11 +34,11 @@ OpDecorate %17 ArrayStride 16 %24 = OpConstant %5 44 %25 = OpConstant %5 45 %26 = OpConstantComposite %6 %24 %25 -%27 = OpConstant %7 46.0 -%28 = OpConstant %7 47.0 +%27 = OpConstant %7 46 +%28 = OpConstant %7 47 %29 = OpConstantComposite %8 %27 %28 -%30 = OpConstant %7 48.0 -%31 = OpConstant %7 49.0 +%30 = OpConstant %7 48 +%31 = OpConstant %7 49 %32 = OpConstantComposite %8 %30 %31 %33 = OpConstant %5 42 %34 = OpConstant %5 43 @@ -47,14 +47,14 @@ OpDecorate %17 ArrayStride 16 %37 = OpConstantComposite %4 %36 %36 %38 = OpConstant %5 0 %39 = OpConstantComposite %6 %38 %38 -%40 = OpConstant %7 0.0 +%40 = OpConstant %7 0 %41 = OpConstantComposite %8 %40 %40 %42 = OpConstantComposite %9 %41 %41 -%43 = OpConstant %7 1.0 -%44 = OpConstant %7 2.0 +%43 = OpConstant %7 1 +%44 = OpConstant %7 2 %45 = OpConstantComposite %8 %43 %44 -%46 = OpConstant %7 3.0 -%47 = OpConstant %7 4.0 +%46 = OpConstant %7 3 +%47 = OpConstant %7 4 %48 = OpConstantComposite %8 %46 %47 %49 = OpConstantComposite %9 %45 %48 %50 = OpConstant %3 1 diff --git a/naga/tests/out/spv/wgsl-abstract-types-operators.spvasm b/naga/tests/out/spv/wgsl-abstract-types-operators.spvasm index c37f96d92bf..4ce80049d24 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-operators.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-operators.spvasm @@ -15,23 +15,23 @@ OpDecorate %116 BuiltIn LocalInvocationId %5 = OpTypeInt 32 0 %7 = OpConstant %5 64 %6 = OpTypeArray %5 %7 -%8 = OpConstant %3 3.0 +%8 = OpConstant %3 3 %9 = OpConstant %4 3 %10 = OpConstant %5 3 %11 = OpConstant %5 0 %12 = OpConstant %4 -2147483648 -%13 = OpConstant %3 -3.4028235e38 +%13 = OpConstant %3 -340282350000000000000000000000000000000 %14 = OpConstant %4 4 %15 = OpConstant %5 4 %16 = OpConstant %4 0 %18 = OpTypePointer Workgroup %6 %17 = OpVariable %18 Workgroup %21 = OpTypeFunction %2 -%22 = OpConstant %3 42.0 +%22 = OpConstant %3 42 %23 = OpConstant %4 43 %24 = OpConstant %5 44 -%25 = OpConstant %3 1.0 -%26 = OpConstant %3 2.0 +%25 = OpConstant %3 1 +%26 = OpConstant %3 2 %27 = OpConstant %4 1 %28 = OpConstant %4 2 %29 = OpConstant %5 1 @@ -52,8 +52,8 @@ OpDecorate %116 BuiltIn LocalInvocationId %64 = OpConstantNull %5 %66 = OpConstantNull %4 %68 = OpConstantNull %4 -%101 = OpConstant %3 5.0 -%102 = OpConstant %3 7.0 +%101 = OpConstant %3 5 +%102 = OpConstant %3 7 %108 = OpTypePointer Workgroup %5 %114 = OpConstantNull %6 %115 = OpTypeVector %5 3 diff --git a/naga/tests/out/spv/wgsl-abstract-types-return.spvasm b/naga/tests/out/spv/wgsl-abstract-types-return.spvasm index 48c1b155668..d2dc2cfed43 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-return.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-return.spvasm @@ -20,7 +20,7 @@ OpDecorate %7 ArrayStride 4 %16 = OpTypeFunction %4 %17 = OpConstant %4 1 %21 = OpTypeFunction %5 -%22 = OpConstant %5 1.0 +%22 = OpConstant %5 1 %29 = OpTypeFunction %6 %30 = OpConstantComposite %6 %22 %22 %34 = OpTypeFunction %7 diff --git a/naga/tests/out/spv/wgsl-abstract-types-var.spvasm b/naga/tests/out/spv/wgsl-abstract-types-var.spvasm index de7b74dae48..8dc36f0fb5f 100644 --- a/naga/tests/out/spv/wgsl-abstract-types-var.spvasm +++ b/naga/tests/out/spv/wgsl-abstract-types-var.spvasm @@ -35,11 +35,11 @@ OpDecorate %18 ArrayStride 16 %22 = OpConstant %5 44 %23 = OpConstant %5 45 %24 = OpConstantComposite %6 %22 %23 -%25 = OpConstant %7 46.0 -%26 = OpConstant %7 47.0 +%25 = OpConstant %7 46 +%26 = OpConstant %7 47 %27 = OpConstantComposite %8 %25 %26 -%28 = OpConstant %7 48.0 -%29 = OpConstant %7 49.0 +%28 = OpConstant %7 48 +%29 = OpConstant %7 49 %30 = OpConstantComposite %8 %28 %29 %31 = OpConstant %5 42 %32 = OpConstant %5 43 @@ -48,14 +48,14 @@ OpDecorate %18 ArrayStride 16 %35 = OpConstantComposite %4 %34 %34 %36 = OpConstant %5 0 %37 = OpConstantComposite %6 %36 %36 -%38 = OpConstant %7 0.0 +%38 = OpConstant %7 0 %39 = OpConstantComposite %8 %38 %38 %40 = OpConstantComposite %9 %39 %39 -%41 = OpConstant %7 1.0 -%42 = OpConstant %7 2.0 +%41 = OpConstant %7 1 +%42 = OpConstant %7 2 %43 = OpConstantComposite %8 %41 %42 -%44 = OpConstant %7 3.0 -%45 = OpConstant %7 4.0 +%44 = OpConstant %7 3 +%45 = OpConstant %7 4 %46 = OpConstantComposite %8 %44 %45 %47 = OpConstantComposite %9 %43 %46 %48 = OpConstant %3 1 diff --git a/naga/tests/out/spv/wgsl-access.spvasm b/naga/tests/out/spv/wgsl-access.spvasm index 8bbfd03edba..31e8e5d4c0b 100644 --- a/naga/tests/out/spv/wgsl-access.spvasm +++ b/naga/tests/out/spv/wgsl-access.spvasm @@ -1,16 +1,16 @@ ; SPIR-V ; Version: 1.1 ; Generator: rspirv -; Bound: 416 +; Bound: 414 OpCapability Shader OpExtension "SPV_KHR_storage_buffer_storage_class" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint Vertex %333 "foo_vert" %328 %331 -OpEntryPoint Fragment %389 "foo_frag" %388 -OpEntryPoint GLCompute %407 "foo_compute" -OpExecutionMode %389 OriginUpperLeft -OpExecutionMode %407 LocalSize 1 1 1 +OpEntryPoint Vertex %331 "foo_vert" %326 %329 +OpEntryPoint Fragment %387 "foo_frag" %386 +OpEntryPoint GLCompute %405 "foo_compute" +OpExecutionMode %387 OriginUpperLeft +OpExecutionMode %405 LocalSize 1 1 1 %3 = OpString "access.wgsl" OpSource Unknown 0 %3 "// This snapshot tests accessing various containers, dereferencing pointers. @@ -49,6 +49,9 @@ var baz: Baz; var qux: vec2; fn test_matrix_within_struct_accesses() { + // Test HLSL accesses to Cx2 matrices. There are additional tests + // in `hlsl_mat_cx2.wgsl`. + var idx = 1; idx--; @@ -331,16 +334,16 @@ OpName %275 "a" OpName %284 "member_ptr" OpName %288 "s" OpName %294 "let_members_of_members" -OpName %306 "var_members_of_members" -OpName %307 "thing" -OpName %309 "inner" -OpName %312 "delishus" -OpName %328 "vi" -OpName %333 "foo_vert" -OpName %344 "foo" -OpName %345 "c2" -OpName %389 "foo_frag" -OpName %407 "foo_compute" +OpName %305 "var_members_of_members" +OpName %306 "thing" +OpName %308 "inner" +OpName %311 "delishus" +OpName %326 "vi" +OpName %331 "foo_vert" +OpName %342 "foo" +OpName %343 "c2" +OpName %387 "foo_frag" +OpName %405 "foo_compute" OpMemberDecorate %7 0 Offset 0 OpMemberDecorate %7 1 Offset 16 OpMemberDecorate %7 2 Offset 28 @@ -392,9 +395,9 @@ OpDecorate %62 DescriptorSet 0 OpDecorate %62 Binding 3 OpDecorate %63 Block OpMemberDecorate %63 0 Offset 0 -OpDecorate %328 BuiltIn VertexIndex -OpDecorate %331 BuiltIn Position -OpDecorate %388 Location 0 +OpDecorate %326 BuiltIn VertexIndex +OpDecorate %329 BuiltIn Position +OpDecorate %386 Location 0 %2 = OpTypeVoid %4 = OpTypeInt 32 0 %5 = OpTypeVector %4 3 @@ -460,29 +463,29 @@ OpDecorate %388 Location 0 %67 = OpTypeFunction %2 %68 = OpTypePointer Uniform %23 %70 = OpConstant %6 1 -%71 = OpConstant %9 1.0 +%71 = OpConstant %9 1 %72 = OpConstantComposite %13 %71 %71 -%73 = OpConstant %9 2.0 +%73 = OpConstant %9 2 %74 = OpConstantComposite %13 %73 %73 -%75 = OpConstant %9 3.0 +%75 = OpConstant %9 3 %76 = OpConstantComposite %13 %75 %75 %77 = OpConstantComposite %22 %72 %74 %76 %78 = OpConstantComposite %23 %77 -%79 = OpConstant %9 6.0 +%79 = OpConstant %9 6 %80 = OpConstantComposite %13 %79 %79 -%81 = OpConstant %9 5.0 +%81 = OpConstant %9 5 %82 = OpConstantComposite %13 %81 %81 -%83 = OpConstant %9 4.0 +%83 = OpConstant %9 4 %84 = OpConstantComposite %13 %83 %83 %85 = OpConstantComposite %22 %80 %82 %84 -%86 = OpConstant %9 9.0 +%86 = OpConstant %9 9 %87 = OpConstantComposite %13 %86 %86 -%88 = OpConstant %9 90.0 +%88 = OpConstant %9 90 %89 = OpConstantComposite %13 %88 %88 -%90 = OpConstant %9 10.0 -%91 = OpConstant %9 20.0 -%92 = OpConstant %9 30.0 -%93 = OpConstant %9 40.0 +%90 = OpConstant %9 10 +%91 = OpConstant %9 20 +%92 = OpConstant %9 30 +%93 = OpConstant %9 40 %95 = OpTypePointer Function %6 %97 = OpTypePointer Function %23 %101 = OpTypePointer Uniform %22 @@ -493,9 +496,9 @@ OpDecorate %388 Location 0 %141 = OpTypePointer Uniform %27 %143 = OpConstantNull %26 %144 = OpConstantComposite %27 %143 -%145 = OpConstant %9 8.0 +%145 = OpConstant %9 8 %146 = OpConstantComposite %13 %145 %145 -%147 = OpConstant %9 7.0 +%147 = OpConstant %9 7 %148 = OpConstantComposite %13 %147 %147 %149 = OpConstantComposite %25 %146 %148 %80 %82 %152 = OpTypePointer Function %27 @@ -530,45 +533,45 @@ OpDecorate %388 Location 0 %287 = OpConstantComposite %45 %286 %289 = OpTypePointer Function %45 %295 = OpConstantNull %47 -%308 = OpTypePointer Function %47 -%310 = OpTypePointer Function %46 -%311 = OpConstantNull %46 -%313 = OpConstantNull %6 -%329 = OpTypePointer Input %4 -%328 = OpVariable %329 Input -%332 = OpTypePointer Output %32 -%331 = OpVariable %332 Output -%335 = OpTypePointer StorageBuffer %24 -%338 = OpConstant %9 0.0 -%339 = OpConstant %4 3 -%340 = OpConstant %6 3 -%341 = OpConstant %6 4 -%342 = OpConstant %6 5 -%343 = OpConstantNull %30 -%346 = OpTypePointer Function %33 -%347 = OpConstantNull %33 -%353 = OpTypePointer StorageBuffer %10 -%356 = OpTypePointer StorageBuffer %19 -%359 = OpTypePointer StorageBuffer %11 -%360 = OpTypePointer StorageBuffer %9 -%363 = OpTypePointer StorageBuffer %20 -%366 = OpTypePointer StorageBuffer %8 -%367 = OpTypePointer StorageBuffer %6 -%372 = OpConstant %9 -2147483600.0 -%373 = OpConstant %9 2147483500.0 -%382 = OpTypeVector %6 4 -%388 = OpVariable %332 Output -%391 = OpConstantComposite %11 %338 %338 %338 -%392 = OpConstantComposite %11 %71 %71 %71 -%393 = OpConstantComposite %11 %73 %73 %73 -%394 = OpConstantComposite %11 %75 %75 %75 -%395 = OpConstantComposite %10 %391 %392 %393 %394 -%396 = OpConstantComposite %18 %48 %48 -%397 = OpConstantComposite %18 %44 %44 -%398 = OpConstantComposite %19 %396 %397 -%399 = OpConstantNull %24 -%400 = OpConstantComposite %32 %338 %338 %338 %338 -%408 = OpConstantTrue %42 +%307 = OpTypePointer Function %47 +%309 = OpTypePointer Function %46 +%310 = OpConstantNull %46 +%312 = OpConstantNull %6 +%327 = OpTypePointer Input %4 +%326 = OpVariable %327 Input +%330 = OpTypePointer Output %32 +%329 = OpVariable %330 Output +%333 = OpTypePointer StorageBuffer %24 +%336 = OpConstant %9 0 +%337 = OpConstant %4 3 +%338 = OpConstant %6 3 +%339 = OpConstant %6 4 +%340 = OpConstant %6 5 +%341 = OpConstantNull %30 +%344 = OpTypePointer Function %33 +%345 = OpConstantNull %33 +%351 = OpTypePointer StorageBuffer %10 +%354 = OpTypePointer StorageBuffer %19 +%357 = OpTypePointer StorageBuffer %11 +%358 = OpTypePointer StorageBuffer %9 +%361 = OpTypePointer StorageBuffer %20 +%364 = OpTypePointer StorageBuffer %8 +%365 = OpTypePointer StorageBuffer %6 +%370 = OpConstant %9 -2147483600 +%371 = OpConstant %9 2147483500 +%380 = OpTypeVector %6 4 +%386 = OpVariable %330 Output +%389 = OpConstantComposite %11 %336 %336 %336 +%390 = OpConstantComposite %11 %71 %71 %71 +%391 = OpConstantComposite %11 %73 %73 %73 +%392 = OpConstantComposite %11 %75 %75 %75 +%393 = OpConstantComposite %10 %389 %390 %391 %392 +%394 = OpConstantComposite %18 %48 %48 +%395 = OpConstantComposite %18 %44 %44 +%396 = OpConstantComposite %19 %394 %395 +%397 = OpConstantNull %24 +%398 = OpConstantComposite %32 %336 %336 %336 %336 +%406 = OpConstantTrue %42 %66 = OpFunction %2 None %67 %65 = OpLabel %94 = OpVariable %95 Function %70 @@ -576,91 +579,91 @@ OpDecorate %388 Location 0 %69 = OpAccessChain %68 %56 %48 OpBranch %98 %98 = OpLabel -OpLine %3 40 5 +OpLine %3 43 5 %99 = OpLoad %6 %94 %100 = OpISub %6 %99 %70 -OpLine %3 40 5 +OpLine %3 43 5 OpStore %94 %100 -OpLine %3 43 14 +OpLine %3 46 14 %102 = OpAccessChain %101 %69 %48 %103 = OpLoad %22 %102 -OpLine %3 44 14 -OpLine %3 44 14 +OpLine %3 47 14 +OpLine %3 47 14 %105 = OpAccessChain %104 %69 %48 %48 %106 = OpLoad %13 %105 -OpLine %3 45 14 +OpLine %3 48 14 %107 = OpLoad %6 %94 %108 = OpAccessChain %104 %69 %48 %107 %109 = OpLoad %13 %108 -OpLine %3 46 14 -OpLine %3 46 14 -OpLine %3 46 14 +OpLine %3 49 14 +OpLine %3 49 14 +OpLine %3 49 14 %111 = OpAccessChain %110 %69 %48 %48 %44 %112 = OpLoad %9 %111 -OpLine %3 47 14 -OpLine %3 47 14 +OpLine %3 50 14 +OpLine %3 50 14 %113 = OpLoad %6 %94 %114 = OpAccessChain %110 %69 %48 %48 %113 %115 = OpLoad %9 %114 -OpLine %3 48 14 +OpLine %3 51 14 %116 = OpLoad %6 %94 -OpLine %3 48 14 +OpLine %3 51 14 %117 = OpAccessChain %110 %69 %48 %116 %44 %118 = OpLoad %9 %117 -OpLine %3 49 14 +OpLine %3 52 14 %119 = OpLoad %6 %94 %120 = OpLoad %6 %94 %121 = OpAccessChain %110 %69 %48 %119 %120 %122 = OpLoad %9 %121 -OpLine %3 51 29 -OpLine %3 51 45 -OpLine %3 51 13 -OpLine %3 53 5 +OpLine %3 54 29 +OpLine %3 54 45 +OpLine %3 54 13 +OpLine %3 56 5 %123 = OpLoad %6 %94 %124 = OpIAdd %6 %123 %70 -OpLine %3 53 5 -OpStore %94 %124 -OpLine %3 56 5 -OpLine %3 56 23 -OpLine %3 56 39 -OpLine %3 56 11 OpLine %3 56 5 +OpStore %94 %124 +OpLine %3 59 5 +OpLine %3 59 23 +OpLine %3 59 39 +OpLine %3 59 11 +OpLine %3 59 5 %126 = OpAccessChain %125 %96 %48 OpStore %126 %85 -OpLine %3 57 5 -OpLine %3 57 5 -OpLine %3 57 14 -OpLine %3 57 5 +OpLine %3 60 5 +OpLine %3 60 5 +OpLine %3 60 14 +OpLine %3 60 5 %128 = OpAccessChain %127 %96 %48 %48 OpStore %128 %87 -OpLine %3 58 5 +OpLine %3 61 5 %129 = OpLoad %6 %94 -OpLine %3 58 16 -OpLine %3 58 5 +OpLine %3 61 16 +OpLine %3 61 5 %130 = OpAccessChain %127 %96 %48 %129 OpStore %130 %89 -OpLine %3 59 5 -OpLine %3 59 5 -OpLine %3 59 5 -OpLine %3 59 5 +OpLine %3 62 5 +OpLine %3 62 5 +OpLine %3 62 5 +OpLine %3 62 5 %131 = OpAccessChain %28 %96 %48 %48 %44 OpStore %131 %90 -OpLine %3 60 5 -OpLine %3 60 5 +OpLine %3 63 5 +OpLine %3 63 5 %132 = OpLoad %6 %94 -OpLine %3 60 5 +OpLine %3 63 5 %133 = OpAccessChain %28 %96 %48 %48 %132 OpStore %133 %91 -OpLine %3 61 5 +OpLine %3 64 5 %134 = OpLoad %6 %94 -OpLine %3 61 5 -OpLine %3 61 5 +OpLine %3 64 5 +OpLine %3 64 5 %135 = OpAccessChain %28 %96 %48 %134 %44 OpStore %135 %92 -OpLine %3 62 5 +OpLine %3 65 5 %136 = OpLoad %6 %94 %137 = OpLoad %6 %94 -OpLine %3 62 5 +OpLine %3 65 5 %138 = OpAccessChain %28 %96 %48 %136 %137 OpStore %138 %93 OpReturn @@ -672,111 +675,111 @@ OpFunctionEnd %142 = OpAccessChain %141 %62 %48 OpBranch %153 %153 = OpLabel -OpLine %3 75 5 +OpLine %3 78 5 %154 = OpLoad %6 %150 %155 = OpISub %6 %154 %70 -OpLine %3 75 5 +OpLine %3 78 5 OpStore %150 %155 -OpLine %3 78 14 +OpLine %3 81 14 %157 = OpAccessChain %156 %142 %48 %158 = OpLoad %26 %157 -OpLine %3 79 14 -OpLine %3 79 14 +OpLine %3 82 14 +OpLine %3 82 14 %160 = OpAccessChain %159 %142 %48 %48 %161 = OpLoad %25 %160 -OpLine %3 80 14 -OpLine %3 80 14 -OpLine %3 80 14 +OpLine %3 83 14 +OpLine %3 83 14 +OpLine %3 83 14 %162 = OpAccessChain %104 %142 %48 %48 %48 %163 = OpLoad %13 %162 -OpLine %3 81 14 -OpLine %3 81 14 +OpLine %3 84 14 +OpLine %3 84 14 %164 = OpLoad %6 %150 %165 = OpAccessChain %104 %142 %48 %48 %164 %166 = OpLoad %13 %165 -OpLine %3 82 14 -OpLine %3 82 14 -OpLine %3 82 14 -OpLine %3 82 14 +OpLine %3 85 14 +OpLine %3 85 14 +OpLine %3 85 14 +OpLine %3 85 14 %167 = OpAccessChain %110 %142 %48 %48 %48 %44 %168 = OpLoad %9 %167 -OpLine %3 83 14 -OpLine %3 83 14 -OpLine %3 83 14 +OpLine %3 86 14 +OpLine %3 86 14 +OpLine %3 86 14 %169 = OpLoad %6 %150 %170 = OpAccessChain %110 %142 %48 %48 %48 %169 %171 = OpLoad %9 %170 -OpLine %3 84 14 -OpLine %3 84 14 +OpLine %3 87 14 +OpLine %3 87 14 %172 = OpLoad %6 %150 -OpLine %3 84 14 +OpLine %3 87 14 %173 = OpAccessChain %110 %142 %48 %48 %172 %44 %174 = OpLoad %9 %173 -OpLine %3 85 14 -OpLine %3 85 14 +OpLine %3 88 14 +OpLine %3 88 14 %175 = OpLoad %6 %150 %176 = OpLoad %6 %150 %177 = OpAccessChain %110 %142 %48 %48 %175 %176 %178 = OpLoad %9 %177 -OpLine %3 87 13 -OpLine %3 89 5 +OpLine %3 90 13 +OpLine %3 92 5 %179 = OpLoad %6 %150 %180 = OpIAdd %6 %179 %70 -OpLine %3 89 5 -OpStore %150 %180 -OpLine %3 92 5 OpLine %3 92 5 +OpStore %150 %180 +OpLine %3 95 5 +OpLine %3 95 5 %182 = OpAccessChain %181 %151 %48 OpStore %182 %143 -OpLine %3 93 5 -OpLine %3 93 5 -OpLine %3 93 27 -OpLine %3 93 43 -OpLine %3 93 59 -OpLine %3 93 15 -OpLine %3 93 5 +OpLine %3 96 5 +OpLine %3 96 5 +OpLine %3 96 27 +OpLine %3 96 43 +OpLine %3 96 59 +OpLine %3 96 15 +OpLine %3 96 5 %184 = OpAccessChain %183 %151 %48 %48 OpStore %184 %149 -OpLine %3 94 5 -OpLine %3 94 5 -OpLine %3 94 5 -OpLine %3 94 18 -OpLine %3 94 5 +OpLine %3 97 5 +OpLine %3 97 5 +OpLine %3 97 5 +OpLine %3 97 18 +OpLine %3 97 5 %185 = OpAccessChain %127 %151 %48 %48 %48 OpStore %185 %87 -OpLine %3 95 5 -OpLine %3 95 5 +OpLine %3 98 5 +OpLine %3 98 5 %186 = OpLoad %6 %150 -OpLine %3 95 20 -OpLine %3 95 5 +OpLine %3 98 20 +OpLine %3 98 5 %187 = OpAccessChain %127 %151 %48 %48 %186 OpStore %187 %89 -OpLine %3 96 5 -OpLine %3 96 5 -OpLine %3 96 5 -OpLine %3 96 5 -OpLine %3 96 5 +OpLine %3 99 5 +OpLine %3 99 5 +OpLine %3 99 5 +OpLine %3 99 5 +OpLine %3 99 5 %188 = OpAccessChain %28 %151 %48 %48 %48 %44 OpStore %188 %90 -OpLine %3 97 5 -OpLine %3 97 5 -OpLine %3 97 5 +OpLine %3 100 5 +OpLine %3 100 5 +OpLine %3 100 5 %189 = OpLoad %6 %150 -OpLine %3 97 5 +OpLine %3 100 5 %190 = OpAccessChain %28 %151 %48 %48 %48 %189 OpStore %190 %91 -OpLine %3 98 5 -OpLine %3 98 5 +OpLine %3 101 5 +OpLine %3 101 5 %191 = OpLoad %6 %150 -OpLine %3 98 5 -OpLine %3 98 5 +OpLine %3 101 5 +OpLine %3 101 5 %192 = OpAccessChain %28 %151 %48 %48 %191 %44 OpStore %192 %92 -OpLine %3 99 5 -OpLine %3 99 5 +OpLine %3 102 5 +OpLine %3 102 5 %193 = OpLoad %6 %150 %194 = OpLoad %6 %150 -OpLine %3 99 5 +OpLine %3 102 5 %195 = OpAccessChain %28 %151 %48 %48 %193 %194 OpStore %195 %93 OpReturn @@ -786,7 +789,7 @@ OpFunctionEnd %196 = OpLabel OpBranch %200 %200 = OpLabel -OpLine %3 102 22 +OpLine %3 105 22 %201 = OpLoad %9 %197 OpReturnValue %201 OpFunctionEnd @@ -795,9 +798,9 @@ OpFunctionEnd %202 = OpLabel OpBranch %206 %206 = OpLabel -OpLine %3 107 12 +OpLine %3 110 12 %207 = OpCompositeExtract %29 %203 4 -OpLine %3 107 12 +OpLine %3 110 12 %208 = OpCompositeExtract %9 %207 9 OpReturnValue %208 OpFunctionEnd @@ -806,7 +809,7 @@ OpFunctionEnd %209 = OpLabel OpBranch %214 %214 = OpLabel -OpLine %3 156 5 +OpLine %3 159 5 OpStore %210 %213 OpReturn OpFunctionEnd @@ -815,11 +818,11 @@ OpFunctionEnd %215 = OpLabel OpBranch %222 %222 = OpLabel -OpLine %3 160 32 -OpLine %3 160 43 -OpLine %3 160 32 -OpLine %3 160 12 -OpLine %3 160 5 +OpLine %3 163 32 +OpLine %3 163 43 +OpLine %3 163 32 +OpLine %3 163 12 +OpLine %3 163 5 OpStore %216 %221 OpReturn OpFunctionEnd @@ -829,13 +832,13 @@ OpFunctionEnd %230 = OpVariable %36 Function %228 OpBranch %231 %231 = OpLabel -OpLine %3 165 5 -%232 = OpFunctionCall %2 %211 %229 -OpLine %3 167 35 -OpLine %3 167 46 -OpLine %3 167 35 -OpLine %3 167 15 OpLine %3 168 5 +%232 = OpFunctionCall %2 %211 %229 +OpLine %3 170 35 +OpLine %3 170 46 +OpLine %3 170 35 +OpLine %3 170 15 +OpLine %3 171 5 %233 = OpFunctionCall %2 %217 %230 OpReturn OpFunctionEnd @@ -844,7 +847,7 @@ OpFunctionEnd %234 = OpLabel OpBranch %238 %238 = OpLabel -OpLine %3 176 10 +OpLine %3 179 10 %239 = OpAccessChain %34 %235 %48 %240 = OpLoad %4 %239 OpReturnValue %240 @@ -854,8 +857,8 @@ OpFunctionEnd %241 = OpLabel OpBranch %245 %245 = OpLabel -OpLine %3 180 3 -OpLine %3 180 3 +OpLine %3 183 3 +OpLine %3 183 3 %246 = OpAccessChain %34 %242 %48 OpStore %246 %17 OpReturn @@ -865,7 +868,7 @@ OpFunctionEnd %247 = OpLabel OpBranch %251 %251 = OpLabel -OpLine %3 184 10 +OpLine %3 187 10 %252 = OpAccessChain %34 %248 %44 %253 = OpLoad %4 %252 OpReturnValue %253 @@ -875,8 +878,8 @@ OpFunctionEnd %254 = OpLabel OpBranch %258 %258 = OpLabel -OpLine %3 188 3 -OpLine %3 188 3 +OpLine %3 191 3 +OpLine %3 191 3 %259 = OpAccessChain %34 %255 %44 OpStore %259 %17 OpReturn @@ -887,13 +890,13 @@ OpFunctionEnd %264 = OpVariable %41 Function %265 OpBranch %266 %266 = OpLabel -OpLine %3 193 4 +OpLine %3 196 4 %267 = OpFunctionCall %2 %243 %262 -OpLine %3 194 4 -%268 = OpFunctionCall %4 %236 %262 OpLine %3 197 4 +%268 = OpFunctionCall %4 %236 %262 +OpLine %3 200 4 %269 = OpFunctionCall %2 %256 %264 -OpLine %3 198 4 +OpLine %3 201 4 %270 = OpFunctionCall %4 %249 %264 OpReturn OpFunctionEnd @@ -903,11 +906,11 @@ OpFunctionEnd %275 = OpVariable %276 Function %277 OpBranch %278 %278 = OpLabel -OpLine %3 202 13 +OpLine %3 205 13 %279 = OpCompositeConstruct %43 %272 -OpLine %3 202 5 +OpLine %3 205 5 OpStore %275 %279 -OpLine %3 204 12 +OpLine %3 207 12 %281 = OpAccessChain %280 %275 %48 %282 = OpLoad %42 %281 OpReturnValue %282 @@ -917,8 +920,8 @@ OpFunctionEnd %288 = OpVariable %289 Function %287 OpBranch %290 %290 = OpLabel -OpLine %3 210 16 -OpLine %3 212 12 +OpLine %3 213 16 +OpLine %3 215 12 %291 = OpAccessChain %95 %288 %48 %292 = OpLoad %6 %291 OpReturnValue %292 @@ -927,173 +930,167 @@ OpFunctionEnd %293 = OpLabel OpBranch %296 %296 = OpLabel -OpLine %3 222 17 +OpLine %3 225 17 %297 = OpCompositeExtract %46 %295 0 -OpLine %3 223 20 +OpLine %3 226 20 %298 = OpCompositeExtract %6 %297 0 -OpLine %3 225 9 +OpLine %3 228 9 %299 = OpCompositeExtract %4 %295 1 %300 = OpBitcast %4 %298 %301 = OpINotEqual %42 %299 %300 -OpLine %3 225 5 -OpSelectionMerge %302 None -OpBranchConditional %301 %302 %302 -%302 = OpLabel -OpLine %3 229 12 -%303 = OpCompositeExtract %46 %295 0 -%304 = OpCompositeExtract %6 %303 0 -OpReturnValue %304 +OpLine %3 228 5 +OpLine %3 232 12 +%302 = OpCompositeExtract %46 %295 0 +%303 = OpCompositeExtract %6 %302 0 +OpReturnValue %303 OpFunctionEnd -%306 = OpFunction %6 None %285 -%305 = OpLabel -%307 = OpVariable %308 Function %295 -%309 = OpVariable %310 Function %311 -%312 = OpVariable %95 Function %313 -OpBranch %314 -%314 = OpLabel -OpLine %3 235 17 -%315 = OpAccessChain %310 %307 %48 -%316 = OpLoad %46 %315 -OpLine %3 235 5 -OpStore %309 %316 -OpLine %3 236 20 -%317 = OpAccessChain %95 %309 %48 -%318 = OpLoad %6 %317 -OpLine %3 236 5 -OpStore %312 %318 -OpLine %3 238 9 -%319 = OpAccessChain %34 %307 %44 -%320 = OpLoad %4 %319 -%321 = OpLoad %6 %312 -%322 = OpBitcast %4 %321 -%323 = OpINotEqual %42 %320 %322 +%305 = OpFunction %6 None %285 +%304 = OpLabel +%306 = OpVariable %307 Function %295 +%308 = OpVariable %309 Function %310 +%311 = OpVariable %95 Function %312 +OpBranch %313 +%313 = OpLabel +OpLine %3 238 17 +%314 = OpAccessChain %309 %306 %48 +%315 = OpLoad %46 %314 OpLine %3 238 5 -OpSelectionMerge %324 None -OpBranchConditional %323 %324 %324 -%324 = OpLabel -OpLine %3 242 12 -%325 = OpAccessChain %95 %307 %48 %48 -%326 = OpLoad %6 %325 -OpReturnValue %326 +OpStore %308 %315 +OpLine %3 239 20 +%316 = OpAccessChain %95 %308 %48 +%317 = OpLoad %6 %316 +OpLine %3 239 5 +OpStore %311 %317 +OpLine %3 241 9 +%318 = OpAccessChain %34 %306 %44 +%319 = OpLoad %4 %318 +%320 = OpLoad %6 %311 +%321 = OpBitcast %4 %320 +%322 = OpINotEqual %42 %319 %321 +OpLine %3 241 5 +OpLine %3 245 12 +%323 = OpAccessChain %95 %306 %48 %48 +%324 = OpLoad %6 %323 +OpReturnValue %324 OpFunctionEnd -%333 = OpFunction %2 None %67 -%327 = OpLabel -%344 = OpVariable %28 Function %338 -%345 = OpVariable %346 Function %347 -%330 = OpLoad %4 %328 -%334 = OpAccessChain %68 %56 %48 -%336 = OpAccessChain %335 %59 %48 -%337 = OpAccessChain %141 %62 %48 -OpBranch %348 -%348 = OpLabel +%331 = OpFunction %2 None %67 +%325 = OpLabel +%342 = OpVariable %28 Function %336 +%343 = OpVariable %344 Function %345 +%328 = OpLoad %4 %326 +%332 = OpAccessChain %68 %56 %48 +%334 = OpAccessChain %333 %59 %48 +%335 = OpAccessChain %141 %62 %48 +OpBranch %346 +%346 = OpLabel OpLine %3 1 1 -%349 = OpLoad %9 %344 -OpLine %3 115 5 -OpStore %344 %71 -OpLine %3 117 9 -%350 = OpLoad %7 %52 +%347 = OpLoad %9 %342 OpLine %3 118 5 -%351 = OpFunctionCall %2 %66 -OpLine %3 119 5 -%352 = OpFunctionCall %2 %140 -OpLine %3 122 19 -%354 = OpAccessChain %353 %54 %48 -%355 = OpLoad %10 %354 -OpLine %3 123 15 -%357 = OpAccessChain %356 %54 %40 -%358 = OpLoad %19 %357 -OpLine %3 125 13 -%361 = OpAccessChain %360 %54 %48 %339 %48 -%362 = OpLoad %9 %361 -OpLine %3 126 13 -OpLine %3 126 22 -%364 = OpArrayLength %4 %54 5 -OpLine %3 126 13 -%365 = OpISub %4 %364 %15 -%368 = OpAccessChain %367 %54 %31 %365 %48 -%369 = OpLoad %6 %368 -OpLine %3 127 13 -%370 = OpLoad %24 %336 -OpLine %3 130 56 -OpLine %3 130 56 -OpLine %3 131 21 -%371 = OpFunctionCall %9 %198 %344 -OpLine %3 134 31 -%374 = OpExtInst %9 %1 FClamp %362 %372 %373 -%375 = OpConvertFToS %6 %374 -OpLine %3 134 14 -%376 = OpCompositeConstruct %33 %369 %375 %340 %341 %342 -OpLine %3 134 5 -OpStore %345 %376 -OpLine %3 135 5 -%377 = OpIAdd %4 %330 %44 -OpLine %3 135 5 -%378 = OpAccessChain %95 %345 %377 -OpStore %378 %286 -OpLine %3 136 17 -%379 = OpAccessChain %95 %345 %330 -%380 = OpLoad %6 %379 +OpStore %342 %71 +OpLine %3 120 9 +%348 = OpLoad %7 %52 +OpLine %3 121 5 +%349 = OpFunctionCall %2 %66 +OpLine %3 122 5 +%350 = OpFunctionCall %2 %140 +OpLine %3 125 19 +%352 = OpAccessChain %351 %54 %48 +%353 = OpLoad %10 %352 +OpLine %3 126 15 +%355 = OpAccessChain %354 %54 %40 +%356 = OpLoad %19 %355 +OpLine %3 128 13 +%359 = OpAccessChain %358 %54 %48 %337 %48 +%360 = OpLoad %9 %359 +OpLine %3 129 13 +OpLine %3 129 22 +%362 = OpArrayLength %4 %54 5 +OpLine %3 129 13 +%363 = OpISub %4 %362 %15 +%366 = OpAccessChain %365 %54 %31 %363 %48 +%367 = OpLoad %6 %366 +OpLine %3 130 13 +%368 = OpLoad %24 %334 +OpLine %3 133 56 +OpLine %3 133 56 +OpLine %3 134 21 +%369 = OpFunctionCall %9 %198 %342 +OpLine %3 137 31 +%372 = OpExtInst %9 %1 FClamp %360 %370 %371 +%373 = OpConvertFToS %6 %372 +OpLine %3 137 14 +%374 = OpCompositeConstruct %33 %367 %373 %338 %339 %340 +OpLine %3 137 5 +OpStore %343 %374 +OpLine %3 138 5 +%375 = OpIAdd %4 %328 %44 OpLine %3 138 5 -%381 = OpFunctionCall %9 %204 %343 -OpLine %3 140 22 -%383 = OpCompositeConstruct %382 %380 %380 %380 %380 -%384 = OpConvertSToF %32 %383 -%385 = OpMatrixTimesVector %11 %355 %384 -OpLine %3 140 12 -%386 = OpCompositeConstruct %32 %385 %73 -OpStore %331 %386 +%376 = OpAccessChain %95 %343 %375 +OpStore %376 %286 +OpLine %3 139 17 +%377 = OpAccessChain %95 %343 %328 +%378 = OpLoad %6 %377 +OpLine %3 141 5 +%379 = OpFunctionCall %9 %204 %341 +OpLine %3 143 22 +%381 = OpCompositeConstruct %380 %378 %378 %378 %378 +%382 = OpConvertSToF %32 %381 +%383 = OpMatrixTimesVector %11 %353 %382 +OpLine %3 143 12 +%384 = OpCompositeConstruct %32 %383 %73 +OpStore %329 %384 OpReturn OpFunctionEnd -%389 = OpFunction %2 None %67 -%387 = OpLabel -%390 = OpAccessChain %335 %59 %48 -OpBranch %401 -%401 = OpLabel -OpLine %3 146 5 -OpLine %3 146 5 -OpLine %3 146 5 -%402 = OpAccessChain %360 %54 %48 %44 %15 -OpStore %402 %71 -OpLine %3 147 5 -OpLine %3 147 31 -OpLine %3 147 47 -OpLine %3 147 63 -OpLine %3 147 19 -OpLine %3 147 5 -%403 = OpAccessChain %353 %54 %48 -OpStore %403 %395 -OpLine %3 148 5 -OpLine %3 148 35 -OpLine %3 148 15 -OpLine %3 148 5 -%404 = OpAccessChain %356 %54 %40 -OpStore %404 %398 +%387 = OpFunction %2 None %67 +%385 = OpLabel +%388 = OpAccessChain %333 %59 %48 +OpBranch %399 +%399 = OpLabel OpLine %3 149 5 OpLine %3 149 5 OpLine %3 149 5 -%405 = OpAccessChain %367 %54 %31 %44 %48 -OpStore %405 %70 +%400 = OpAccessChain %358 %54 %48 %44 %15 +OpStore %400 %71 +OpLine %3 150 5 +OpLine %3 150 31 +OpLine %3 150 47 +OpLine %3 150 63 +OpLine %3 150 19 OpLine %3 150 5 -OpStore %390 %399 -OpLine %3 152 12 -OpStore %388 %400 +%401 = OpAccessChain %351 %54 %48 +OpStore %401 %393 +OpLine %3 151 5 +OpLine %3 151 35 +OpLine %3 151 15 +OpLine %3 151 5 +%402 = OpAccessChain %354 %54 %40 +OpStore %402 %396 +OpLine %3 152 5 +OpLine %3 152 5 +OpLine %3 152 5 +%403 = OpAccessChain %365 %54 %31 %44 %48 +OpStore %403 %70 +OpLine %3 153 5 +OpStore %388 %397 +OpLine %3 155 12 +OpStore %386 %398 OpReturn OpFunctionEnd -%407 = OpFunction %2 None %67 -%406 = OpLabel -OpBranch %409 -%409 = OpLabel -OpLine %3 247 5 -%410 = OpFunctionCall %2 %224 -OpLine %3 248 5 -%411 = OpFunctionCall %2 %261 -OpLine %3 249 5 -%412 = OpFunctionCall %42 %273 %408 +%405 = OpFunction %2 None %67 +%404 = OpLabel +OpBranch %407 +%407 = OpLabel OpLine %3 250 5 -%413 = OpFunctionCall %6 %284 +%408 = OpFunctionCall %2 %224 OpLine %3 251 5 -%414 = OpFunctionCall %6 %294 +%409 = OpFunctionCall %2 %261 OpLine %3 252 5 -%415 = OpFunctionCall %6 %306 +%410 = OpFunctionCall %42 %273 %406 +OpLine %3 253 5 +%411 = OpFunctionCall %6 %284 +OpLine %3 254 5 +%412 = OpFunctionCall %6 %294 +OpLine %3 255 5 +%413 = OpFunctionCall %6 %305 OpReturn OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/spv/wgsl-aliased-ray-query.spvasm b/naga/tests/out/spv/wgsl-aliased-ray-query.spvasm index 2dc19683634..b095e8b8e83 100644 --- a/naga/tests/out/spv/wgsl-aliased-ray-query.spvasm +++ b/naga/tests/out/spv/wgsl-aliased-ray-query.spvasm @@ -34,7 +34,7 @@ OpDecorate %13 DescriptorSet 0 OpDecorate %13 Binding 0 %2 = OpTypeVoid %3 = OpTypeRayQueryKHR -%4 = OpTypeAccelerationStructureNV +%4 = OpTypeAccelerationStructureKHR %5 = OpTypeFloat 32 %6 = OpTypeVector %5 3 %7 = OpTypeInt 32 0 @@ -46,17 +46,17 @@ OpDecorate %13 Binding 0 %14 = OpTypePointer UniformConstant %4 %13 = OpVariable %14 UniformConstant %17 = OpTypeFunction %2 -%19 = OpConstant %5 0.0 +%19 = OpConstant %5 0 %20 = OpConstantComposite %6 %19 %19 %19 -%21 = OpConstant %5 1.0 +%21 = OpConstant %5 1 %22 = OpConstantComposite %6 %19 %21 %19 %23 = OpConstant %7 4 %24 = OpConstant %7 255 %25 = OpConstant %5 0.1 -%26 = OpConstant %5 100.0 +%26 = OpConstant %5 100 %27 = OpConstantComposite %8 %23 %24 %25 %26 %20 %22 %28 = OpConstant %7 3 -%29 = OpConstant %5 10.0 +%29 = OpConstant %5 10 %30 = OpConstant %7 1 %32 = OpTypePointer Function %3 %40 = OpTypePointer Function %12 diff --git a/naga/tests/out/spv/wgsl-array-in-function-return-type.spvasm b/naga/tests/out/spv/wgsl-array-in-function-return-type.spvasm index 146e032f354..9e2b2220f25 100644 --- a/naga/tests/out/spv/wgsl-array-in-function-return-type.spvasm +++ b/naga/tests/out/spv/wgsl-array-in-function-return-type.spvasm @@ -19,14 +19,14 @@ OpDecorate %26 Location 0 %7 = OpTypeArray %4 %8 %9 = OpTypeVector %3 4 %12 = OpTypeFunction %4 -%13 = OpConstant %3 1.0 -%14 = OpConstant %3 2.0 +%13 = OpConstant %3 1 +%14 = OpConstant %3 2 %15 = OpConstantComposite %4 %13 %14 %19 = OpTypeFunction %7 %27 = OpTypePointer Output %9 %26 = OpVariable %27 Output %29 = OpTypeFunction %2 -%30 = OpConstant %3 0.0 +%30 = OpConstant %3 0 %11 = OpFunction %4 None %12 %10 = OpLabel OpBranch %16 diff --git a/naga/tests/out/spv/wgsl-atomicCompareExchange.spvasm b/naga/tests/out/spv/wgsl-atomicCompareExchange.spvasm index 8b7e4ded087..e0e32c85d82 100644 --- a/naga/tests/out/spv/wgsl-atomicCompareExchange.spvasm +++ b/naga/tests/out/spv/wgsl-atomicCompareExchange.spvasm @@ -44,7 +44,7 @@ OpMemberDecorate %15 0 Offset 0 %21 = OpConstant %3 0 %23 = OpConstantFalse %8 %24 = OpTypeFloat 32 -%25 = OpConstant %24 1.0 +%25 = OpConstant %24 1 %26 = OpConstant %3 1 %28 = OpTypePointer Function %3 %30 = OpTypePointer Function %4 diff --git a/naga/tests/out/spv/wgsl-binding-arrays.spvasm b/naga/tests/out/spv/wgsl-binding-arrays.spvasm index 074aff8f9bc..c95bef4c567 100644 --- a/naga/tests/out/spv/wgsl-binding-arrays.spvasm +++ b/naga/tests/out/spv/wgsl-binding-arrays.spvasm @@ -130,7 +130,7 @@ OpDecorate %395 NonUniform %54 = OpTypePointer Uniform %4 %55 = OpConstant %3 0 %57 = OpConstantComposite %23 %55 %55 -%58 = OpConstant %6 0.0 +%58 = OpConstant %6 0 %59 = OpConstantComposite %22 %58 %58 %58 %58 %60 = OpTypeVector %6 2 %61 = OpConstantComposite %60 %58 %58 diff --git a/naga/tests/out/spv/wgsl-bitcast.spvasm b/naga/tests/out/spv/wgsl-bitcast.spvasm index 43dfa8df339..28b8fe56954 100644 --- a/naga/tests/out/spv/wgsl-bitcast.spvasm +++ b/naga/tests/out/spv/wgsl-bitcast.spvasm @@ -29,7 +29,7 @@ OpExecutionMode %16 LocalSize 1 1 1 %23 = OpConstantComposite %7 %22 %22 %24 = OpConstantComposite %9 %22 %22 %22 %25 = OpConstantComposite %10 %22 %22 %22 %22 -%26 = OpConstant %12 0.0 +%26 = OpConstant %12 0 %27 = OpConstantComposite %11 %26 %26 %28 = OpConstantComposite %13 %26 %26 %26 %29 = OpConstantComposite %14 %26 %26 %26 %26 diff --git a/naga/tests/out/spv/wgsl-bits.spvasm b/naga/tests/out/spv/wgsl-bits.spvasm index dec26768b3a..90786720f1a 100644 --- a/naga/tests/out/spv/wgsl-bits.spvasm +++ b/naga/tests/out/spv/wgsl-bits.spvasm @@ -29,7 +29,7 @@ OpExecutionMode %15 LocalSize 1 1 1 %22 = OpConstantComposite %8 %21 %21 %23 = OpConstantComposite %9 %21 %21 %21 %24 = OpConstantComposite %10 %21 %21 %21 %21 -%25 = OpConstant %12 0.0 +%25 = OpConstant %12 0 %26 = OpConstantComposite %11 %25 %25 %27 = OpConstantComposite %13 %25 %25 %25 %25 %28 = OpConstant %7 5 diff --git a/naga/tests/out/spv/wgsl-boids.spvasm b/naga/tests/out/spv/wgsl-boids.spvasm index 35bda2793b4..837d9326cd4 100644 --- a/naga/tests/out/spv/wgsl-boids.spvasm +++ b/naga/tests/out/spv/wgsl-boids.spvasm @@ -191,14 +191,14 @@ OpDecorate %21 BuiltIn GlobalInvocationId %25 = OpTypeFunction %2 %26 = OpTypePointer Uniform %8 %27 = OpConstant %4 0 -%29 = OpConstant %5 0.0 +%29 = OpConstant %5 0 %30 = OpConstantComposite %6 %29 %29 %31 = OpConstant %12 0 %32 = OpConstant %12 1 %33 = OpConstant %4 1 %34 = OpConstant %5 0.1 -%35 = OpConstant %5 -1.0 -%36 = OpConstant %5 1.0 +%35 = OpConstant %5 -1 +%36 = OpConstant %5 1 %38 = OpTypePointer Function %6 %39 = OpConstantNull %6 %41 = OpConstantNull %6 diff --git a/naga/tests/out/spv/wgsl-bounds-check-image-restrict-depth.spvasm b/naga/tests/out/spv/wgsl-bounds-check-image-restrict-depth.spvasm index b8a36ed3dfc..2f7efbe55df 100644 --- a/naga/tests/out/spv/wgsl-bounds-check-image-restrict-depth.spvasm +++ b/naga/tests/out/spv/wgsl-bounds-check-image-restrict-depth.spvasm @@ -101,7 +101,7 @@ OpDecorate %89 Location 0 %96 = OpConstantNull %7 %97 = OpConstant %6 0 %98 = OpConstant %9 0 -%99 = OpConstant %5 0.0 +%99 = OpConstant %5 0 %100 = OpConstantComposite %11 %99 %99 %99 %99 %21 = OpFunction %5 None %22 %19 = OpFunctionParameter %7 diff --git a/naga/tests/out/spv/wgsl-bounds-check-image-restrict.spvasm b/naga/tests/out/spv/wgsl-bounds-check-image-restrict.spvasm index aa311f557e6..4a7b1240e14 100644 --- a/naga/tests/out/spv/wgsl-bounds-check-image-restrict.spvasm +++ b/naga/tests/out/spv/wgsl-bounds-check-image-restrict.spvasm @@ -229,7 +229,7 @@ OpDecorate %172 Location 0 %187 = OpConstant %11 0 %188 = OpConstantNull %13 %189 = OpConstantNull %7 -%190 = OpConstant %5 0.0 +%190 = OpConstant %5 0 %191 = OpConstantComposite %7 %190 %190 %190 %190 %40 = OpFunction %7 None %41 %38 = OpFunctionParameter %6 diff --git a/naga/tests/out/spv/wgsl-bounds-check-image-rzsw-depth.spvasm b/naga/tests/out/spv/wgsl-bounds-check-image-rzsw-depth.spvasm index 0db821d3bf3..4ecca6c2f38 100644 --- a/naga/tests/out/spv/wgsl-bounds-check-image-rzsw-depth.spvasm +++ b/naga/tests/out/spv/wgsl-bounds-check-image-rzsw-depth.spvasm @@ -100,7 +100,7 @@ OpDecorate %100 Location 0 %107 = OpConstantNull %7 %108 = OpConstant %6 0 %109 = OpConstant %9 0 -%110 = OpConstant %5 0.0 +%110 = OpConstant %5 0 %111 = OpConstantComposite %11 %110 %110 %110 %110 %21 = OpFunction %5 None %22 %19 = OpFunctionParameter %7 diff --git a/naga/tests/out/spv/wgsl-bounds-check-image-rzsw.spvasm b/naga/tests/out/spv/wgsl-bounds-check-image-rzsw.spvasm index 7eb0c8b6121..814d2aa24df 100644 --- a/naga/tests/out/spv/wgsl-bounds-check-image-rzsw.spvasm +++ b/naga/tests/out/spv/wgsl-bounds-check-image-rzsw.spvasm @@ -226,7 +226,7 @@ OpDecorate %187 Location 0 %201 = OpConstantNull %9 %202 = OpConstant %11 0 %203 = OpConstantNull %13 -%204 = OpConstant %5 0.0 +%204 = OpConstant %5 0 %205 = OpConstantComposite %7 %204 %204 %204 %204 %40 = OpFunction %7 None %41 %38 = OpFunctionParameter %6 diff --git a/naga/tests/out/spv/wgsl-bounds-check-restrict.spvasm b/naga/tests/out/spv/wgsl-bounds-check-restrict.spvasm index 9885a0bde73..86a0a0547c6 100644 --- a/naga/tests/out/spv/wgsl-bounds-check-restrict.spvasm +++ b/naga/tests/out/spv/wgsl-bounds-check-restrict.spvasm @@ -45,9 +45,9 @@ OpDecorate %12 Binding 0 %59 = OpTypePointer StorageBuffer %8 %60 = OpConstant %6 2 %68 = OpTypeFunction %3 %11 %11 -%77 = OpConstant %3 100.0 -%83 = OpConstant %3 -2147483600.0 -%84 = OpConstant %3 2147483500.0 +%77 = OpConstant %3 100 +%83 = OpConstant %3 -2147483600 +%84 = OpConstant %3 2147483500 %92 = OpTypeFunction %3 %106 = OpTypeFunction %2 %11 %3 %130 = OpTypeFunction %2 %11 %7 @@ -56,14 +56,14 @@ OpDecorate %12 Binding 0 %170 = OpConstant %6 1000 %184 = OpTypeFunction %2 %185 = OpConstant %11 1 -%186 = OpConstant %3 2.0 -%187 = OpConstant %3 3.0 -%188 = OpConstant %3 4.0 -%189 = OpConstant %3 5.0 +%186 = OpConstant %3 2 +%187 = OpConstant %3 3 +%188 = OpConstant %3 4 +%189 = OpConstant %3 5 %190 = OpConstantComposite %7 %186 %187 %188 %189 %191 = OpConstant %11 6 %192 = OpConstant %11 2 -%193 = OpConstant %3 1.0 +%193 = OpConstant %3 1 %16 = OpFunction %3 None %17 %15 = OpFunctionParameter %11 %14 = OpLabel diff --git a/naga/tests/out/spv/wgsl-bounds-check-zero.spvasm b/naga/tests/out/spv/wgsl-bounds-check-zero.spvasm index c31e9e1ab73..f8cd0080678 100644 --- a/naga/tests/out/spv/wgsl-bounds-check-zero.spvasm +++ b/naga/tests/out/spv/wgsl-bounds-check-zero.spvasm @@ -48,9 +48,9 @@ OpDecorate %12 Binding 0 %74 = OpConstant %6 2 %76 = OpConstantNull %7 %85 = OpTypeFunction %3 %11 %11 -%98 = OpConstant %3 100.0 -%104 = OpConstant %3 -2147483600.0 -%105 = OpConstant %3 2147483500.0 +%98 = OpConstant %3 100 +%104 = OpConstant %3 -2147483600 +%105 = OpConstant %3 2147483500 %116 = OpTypeFunction %3 %118 = OpConstant %6 9 %131 = OpTypeFunction %2 %11 %3 @@ -60,14 +60,14 @@ OpDecorate %12 Binding 0 %206 = OpConstant %6 1000 %224 = OpTypeFunction %2 %225 = OpConstant %11 1 -%226 = OpConstant %3 2.0 -%227 = OpConstant %3 3.0 -%228 = OpConstant %3 4.0 -%229 = OpConstant %3 5.0 +%226 = OpConstant %3 2 +%227 = OpConstant %3 3 +%228 = OpConstant %3 4 +%229 = OpConstant %3 5 %230 = OpConstantComposite %7 %226 %227 %228 %229 %231 = OpConstant %11 6 %232 = OpConstant %11 2 -%233 = OpConstant %3 1.0 +%233 = OpConstant %3 1 %16 = OpFunction %3 None %17 %15 = OpFunctionParameter %11 %14 = OpLabel diff --git a/naga/tests/out/spv/wgsl-const-exprs.spvasm b/naga/tests/out/spv/wgsl-const-exprs.spvasm index c4413460f1e..84701b9e163 100644 --- a/naga/tests/out/spv/wgsl-const-exprs.spvasm +++ b/naga/tests/out/spv/wgsl-const-exprs.spvasm @@ -34,10 +34,10 @@ OpDecorate %13 ArrayStride 4 %24 = OpConstant %7 3.141 %25 = OpConstant %7 6.282 %26 = OpConstant %7 0.44444445 -%27 = OpConstant %7 0.0 +%27 = OpConstant %7 0 %28 = OpConstantComposite %8 %26 %27 %27 %27 -%29 = OpConstant %7 4.0 -%30 = OpConstant %7 5.0 +%29 = OpConstant %7 4 +%30 = OpConstant %7 5 %31 = OpConstantComposite %15 %29 %30 %32 = OpConstantComposite %12 %17 %18 %35 = OpTypeFunction %2 @@ -58,8 +58,8 @@ OpDecorate %13 ArrayStride 4 %86 = OpConstant %3 30 %87 = OpConstant %3 0 %94 = OpConstantNull %3 -%97 = OpConstant %7 1.0 -%98 = OpConstant %7 2.0 +%97 = OpConstant %7 1 +%98 = OpConstant %7 2 %99 = OpConstantComposite %8 %98 %97 %97 %97 %101 = OpTypePointer Function %8 %106 = OpTypePointer Function %9 diff --git a/naga/tests/out/spv/wgsl-constructors.spvasm b/naga/tests/out/spv/wgsl-constructors.spvasm index 233cb2735ea..0192183dc84 100644 --- a/naga/tests/out/spv/wgsl-constructors.spvasm +++ b/naga/tests/out/spv/wgsl-constructors.spvasm @@ -31,11 +31,11 @@ OpDecorate %18 ArrayStride 4 %19 = OpConstant %12 4 %18 = OpTypeArray %5 %19 %20 = OpTypeMatrix %7 2 -%21 = OpConstant %3 0.0 +%21 = OpConstant %3 0 %22 = OpConstantComposite %7 %21 %21 %21 -%23 = OpConstant %3 1.0 -%24 = OpConstant %3 2.0 -%25 = OpConstant %3 3.0 +%23 = OpConstant %3 1 +%24 = OpConstant %3 2 +%25 = OpConstant %3 3 %26 = OpConstantComposite %9 %21 %23 %27 = OpConstantComposite %9 %24 %25 %28 = OpConstantComposite %8 %26 %27 diff --git a/naga/tests/out/spv/wgsl-control-flow.spvasm b/naga/tests/out/spv/wgsl-control-flow.spvasm index 93d82a6588b..e028903c447 100644 --- a/naga/tests/out/spv/wgsl-control-flow.spvasm +++ b/naga/tests/out/spv/wgsl-control-flow.spvasm @@ -1,12 +1,12 @@ ; SPIR-V ; Version: 1.1 ; Generator: rspirv -; Bound: 234 +; Bound: 241 OpCapability Shader %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 -OpEntryPoint GLCompute %224 "main" -OpExecutionMode %224 LocalSize 1 1 1 +OpEntryPoint GLCompute %231 "main" +OpExecutionMode %231 LocalSize 1 1 1 %2 = OpTypeVoid %3 = OpTypeInt 32 1 %6 = OpTypeFunction %2 @@ -24,16 +24,16 @@ OpExecutionMode %224 LocalSize 1 1 1 %20 = OpConstant %12 72 %21 = OpConstant %12 264 %22 = OpConstant %12 2056 -%45 = OpTypeFunction %2 %3 -%82 = OpTypeVector %12 2 -%83 = OpTypePointer Function %82 -%84 = OpTypeBool -%85 = OpTypeVector %84 2 -%86 = OpConstantComposite %82 %13 %13 -%87 = OpConstant %12 4294967295 -%88 = OpConstantComposite %82 %87 %87 -%108 = OpTypeFunction %2 %3 %3 %3 -%177 = OpTypeFunction %2 %3 %3 %3 %3 +%52 = OpTypeFunction %2 %3 +%89 = OpTypeVector %12 2 +%90 = OpTypePointer Function %89 +%91 = OpTypeBool +%92 = OpTypeVector %91 2 +%93 = OpConstantComposite %89 %13 %13 +%94 = OpConstant %12 4294967295 +%95 = OpConstantComposite %89 %94 %94 +%115 = OpTypeFunction %2 %3 %3 %3 +%184 = OpTypeFunction %2 %3 %3 %3 %3 %5 = OpFunction %2 None %6 %4 = OpLabel %14 = OpVariable %15 Function %16 @@ -82,343 +82,362 @@ OpStore %14 %8 OpBranch %36 %38 = OpLabel OpStore %14 %7 -OpReturn +OpBranch %36 %39 = OpLabel OpStore %14 %9 -OpReturn +OpBranch %36 %40 = OpLabel -OpReturn +OpBranch %36 %41 = OpLabel OpStore %14 %10 -OpReturn +OpBranch %36 %36 = OpLabel +%42 = OpLoad %3 %14 +OpSelectionMerge %43 None +OpSwitch %42 %48 1 %44 2 %45 3 %46 4 %46 5 %47 6 %47 +%44 = OpLabel +OpStore %14 %8 +OpReturn +%45 = OpLabel +OpStore %14 %7 OpReturn -OpFunctionEnd -%44 = OpFunction %2 None %45 -%43 = OpFunctionParameter %3 -%42 = OpLabel -OpBranch %46 %46 = OpLabel -OpSelectionMerge %47 None -OpSwitch %43 %48 -%48 = OpLabel -OpBranch %47 +OpStore %14 %9 +OpReturn %47 = OpLabel +OpStore %14 %10 +OpReturn +%48 = OpLabel +OpStore %14 %11 +OpReturn +%43 = OpLabel OpReturn OpFunctionEnd -%50 = OpFunction %2 None %6 +%51 = OpFunction %2 None %52 +%50 = OpFunctionParameter %3 %49 = OpLabel -OpBranch %51 -%51 = OpLabel -OpSelectionMerge %52 None -OpSwitch %8 %54 0 %53 +OpBranch %53 %53 = OpLabel -OpBranch %52 +OpSelectionMerge %54 None +OpSwitch %50 %55 +%55 = OpLabel +OpBranch %54 %54 = OpLabel -OpBranch %52 -%52 = OpLabel OpReturn OpFunctionEnd -%56 = OpFunction %2 None %6 -%55 = OpLabel -OpBranch %57 -%57 = OpLabel -OpSelectionMerge %58 None -OpSwitch %13 %60 0 %59 -%59 = OpLabel -OpBranch %58 -%60 = OpLabel +%57 = OpFunction %2 None %6 +%56 = OpLabel OpBranch %58 %58 = OpLabel -OpSelectionMerge %61 None -OpSwitch %13 %63 0 %62 -%62 = OpLabel -OpReturn -%63 = OpLabel -OpReturn +OpSelectionMerge %59 None +OpSwitch %8 %61 0 %60 +%60 = OpLabel +OpBranch %59 %61 = OpLabel +OpBranch %59 +%59 = OpLabel OpReturn OpFunctionEnd -%65 = OpFunction %2 None %6 +%63 = OpFunction %2 None %6 +%62 = OpLabel +OpBranch %64 %64 = OpLabel -OpBranch %66 +OpSelectionMerge %65 None +OpSwitch %13 %67 0 %66 %66 = OpLabel -OpSelectionMerge %67 None -OpSwitch %8 %73 0 %68 1 %69 2 %70 3 %71 4 %72 -%68 = OpLabel -OpReturn +OpBranch %65 +%67 = OpLabel +OpBranch %65 +%65 = OpLabel +OpSelectionMerge %68 None +OpSwitch %13 %70 0 %69 %69 = OpLabel OpReturn %70 = OpLabel OpReturn -%71 = OpLabel -OpReturn -%72 = OpLabel +%68 = OpLabel OpReturn +OpFunctionEnd +%72 = OpFunction %2 None %6 +%71 = OpLabel +OpBranch %73 %73 = OpLabel +OpSelectionMerge %74 None +OpSwitch %8 %80 0 %75 1 %76 2 %77 3 %78 4 %79 +%75 = OpLabel OpReturn -%67 = OpLabel +%76 = OpLabel OpReturn -OpFunctionEnd -%76 = OpFunction %2 None %45 -%75 = OpFunctionParameter %3 -%74 = OpLabel -%89 = OpVariable %83 Function %88 -OpBranch %77 %77 = OpLabel -OpBranch %78 +OpReturn %78 = OpLabel -OpLoopMerge %79 %81 None -OpBranch %90 -%90 = OpLabel -%91 = OpLoad %82 %89 -%92 = OpIEqual %85 %86 %91 -%93 = OpAll %84 %92 -OpSelectionMerge %94 None -OpBranchConditional %93 %79 %94 -%94 = OpLabel -%95 = OpCompositeExtract %12 %91 1 -%96 = OpIEqual %84 %95 %13 -%97 = OpSelect %12 %96 %19 %13 -%98 = OpCompositeConstruct %82 %97 %19 -%99 = OpISub %82 %91 %98 -OpStore %89 %99 -OpBranch %80 -%80 = OpLabel -OpSelectionMerge %100 None -OpSwitch %75 %102 1 %101 -%101 = OpLabel -OpBranch %81 -%102 = OpLabel -OpBranch %100 -%100 = OpLabel -OpBranch %81 -%81 = OpLabel -OpBranch %78 +OpReturn %79 = OpLabel OpReturn +%80 = OpLabel +OpReturn +%74 = OpLabel +OpReturn OpFunctionEnd -%107 = OpFunction %2 None %108 -%104 = OpFunctionParameter %3 -%105 = OpFunctionParameter %3 -%106 = OpFunctionParameter %3 -%103 = OpLabel -%114 = OpVariable %83 Function %88 -%136 = OpVariable %83 Function %88 -%156 = OpVariable %83 Function %88 -OpBranch %109 +%83 = OpFunction %2 None %52 +%82 = OpFunctionParameter %3 +%81 = OpLabel +%96 = OpVariable %90 Function %95 +OpBranch %84 +%84 = OpLabel +OpBranch %85 +%85 = OpLabel +OpLoopMerge %86 %88 None +OpBranch %97 +%97 = OpLabel +%98 = OpLoad %89 %96 +%99 = OpIEqual %92 %93 %98 +%100 = OpAll %91 %99 +OpSelectionMerge %101 None +OpBranchConditional %100 %86 %101 +%101 = OpLabel +%102 = OpCompositeExtract %12 %98 1 +%103 = OpIEqual %91 %102 %13 +%104 = OpSelect %12 %103 %19 %13 +%105 = OpCompositeConstruct %89 %104 %19 +%106 = OpISub %89 %98 %105 +OpStore %96 %106 +OpBranch %87 +%87 = OpLabel +OpSelectionMerge %107 None +OpSwitch %82 %109 1 %108 +%108 = OpLabel +OpBranch %88 %109 = OpLabel -OpBranch %110 +OpBranch %107 +%107 = OpLabel +OpBranch %88 +%88 = OpLabel +OpBranch %85 +%86 = OpLabel +OpReturn +OpFunctionEnd +%114 = OpFunction %2 None %115 +%111 = OpFunctionParameter %3 +%112 = OpFunctionParameter %3 +%113 = OpFunctionParameter %3 %110 = OpLabel -OpLoopMerge %111 %113 None -OpBranch %115 -%115 = OpLabel -%116 = OpLoad %82 %114 -%117 = OpIEqual %85 %86 %116 -%118 = OpAll %84 %117 -OpSelectionMerge %119 None -OpBranchConditional %118 %111 %119 -%119 = OpLabel -%120 = OpCompositeExtract %12 %116 1 -%121 = OpIEqual %84 %120 %13 -%122 = OpSelect %12 %121 %19 %13 -%123 = OpCompositeConstruct %82 %122 %19 -%124 = OpISub %82 %116 %123 -OpStore %114 %124 -OpBranch %112 -%112 = OpLabel -OpSelectionMerge %125 None -OpSwitch %104 %128 1 %126 2 %127 +%121 = OpVariable %90 Function %95 +%143 = OpVariable %90 Function %95 +%163 = OpVariable %90 Function %95 +OpBranch %116 +%116 = OpLabel +OpBranch %117 +%117 = OpLabel +OpLoopMerge %118 %120 None +OpBranch %122 +%122 = OpLabel +%123 = OpLoad %89 %121 +%124 = OpIEqual %92 %93 %123 +%125 = OpAll %91 %124 +OpSelectionMerge %126 None +OpBranchConditional %125 %118 %126 %126 = OpLabel -OpBranch %113 -%127 = OpLabel -OpSelectionMerge %129 None -OpSwitch %105 %131 1 %130 -%130 = OpLabel -OpBranch %113 -%131 = OpLabel -OpBranch %132 -%132 = OpLabel -OpLoopMerge %133 %135 None -OpBranch %137 -%137 = OpLabel -%138 = OpLoad %82 %136 -%139 = OpIEqual %85 %86 %138 -%140 = OpAll %84 %139 -OpSelectionMerge %141 None -OpBranchConditional %140 %133 %141 -%141 = OpLabel -%142 = OpCompositeExtract %12 %138 1 -%143 = OpIEqual %84 %142 %13 -%144 = OpSelect %12 %143 %19 %13 -%145 = OpCompositeConstruct %82 %144 %19 -%146 = OpISub %82 %138 %145 -OpStore %136 %146 -OpBranch %134 +%127 = OpCompositeExtract %12 %123 1 +%128 = OpIEqual %91 %127 %13 +%129 = OpSelect %12 %128 %19 %13 +%130 = OpCompositeConstruct %89 %129 %19 +%131 = OpISub %89 %123 %130 +OpStore %121 %131 +OpBranch %119 +%119 = OpLabel +OpSelectionMerge %132 None +OpSwitch %111 %135 1 %133 2 %134 +%133 = OpLabel +OpBranch %120 %134 = OpLabel -OpSelectionMerge %147 None -OpSwitch %106 %149 1 %148 +OpSelectionMerge %136 None +OpSwitch %112 %138 1 %137 +%137 = OpLabel +OpBranch %120 +%138 = OpLabel +OpBranch %139 +%139 = OpLabel +OpLoopMerge %140 %142 None +OpBranch %144 +%144 = OpLabel +%145 = OpLoad %89 %143 +%146 = OpIEqual %92 %93 %145 +%147 = OpAll %91 %146 +OpSelectionMerge %148 None +OpBranchConditional %147 %140 %148 %148 = OpLabel -OpBranch %135 -%149 = OpLabel -OpBranch %147 -%147 = OpLabel -OpBranch %135 +%149 = OpCompositeExtract %12 %145 1 +%150 = OpIEqual %91 %149 %13 +%151 = OpSelect %12 %150 %19 %13 +%152 = OpCompositeConstruct %89 %151 %19 +%153 = OpISub %89 %145 %152 +OpStore %143 %153 +OpBranch %141 +%141 = OpLabel +OpSelectionMerge %154 None +OpSwitch %113 %156 1 %155 +%155 = OpLabel +OpBranch %142 +%156 = OpLabel +OpBranch %154 +%154 = OpLabel +OpBranch %142 +%142 = OpLabel +OpBranch %139 +%140 = OpLabel +OpBranch %136 +%136 = OpLabel +OpBranch %132 %135 = OpLabel OpBranch %132 -%133 = OpLabel -OpBranch %129 -%129 = OpLabel -OpBranch %125 -%128 = OpLabel -OpBranch %125 -%125 = OpLabel -OpSelectionMerge %150 None -OpSwitch %105 %151 -%151 = OpLabel -OpBranch %113 -%150 = OpLabel -OpBranch %113 -%113 = OpLabel -OpBranch %110 -%111 = OpLabel -OpBranch %152 -%152 = OpLabel -OpLoopMerge %153 %155 None -OpBranch %157 +%132 = OpLabel +OpSelectionMerge %157 None +OpSwitch %112 %158 +%158 = OpLabel +OpBranch %120 %157 = OpLabel -%158 = OpLoad %82 %156 -%159 = OpIEqual %85 %86 %158 -%160 = OpAll %84 %159 -OpSelectionMerge %161 None -OpBranchConditional %160 %153 %161 -%161 = OpLabel -%162 = OpCompositeExtract %12 %158 1 -%163 = OpIEqual %84 %162 %13 -%164 = OpSelect %12 %163 %19 %13 -%165 = OpCompositeConstruct %82 %164 %19 -%166 = OpISub %82 %158 %165 -OpStore %156 %166 -OpBranch %154 -%154 = OpLabel -OpSelectionMerge %167 None -OpSwitch %105 %168 1 %168 +OpBranch %120 +%120 = OpLabel +OpBranch %117 +%118 = OpLabel +OpBranch %159 +%159 = OpLabel +OpLoopMerge %160 %162 None +OpBranch %164 +%164 = OpLabel +%165 = OpLoad %89 %163 +%166 = OpIEqual %92 %93 %165 +%167 = OpAll %91 %166 +OpSelectionMerge %168 None +OpBranchConditional %167 %160 %168 %168 = OpLabel -OpSelectionMerge %169 None -OpSwitch %106 %170 -%170 = OpLabel -OpBranch %155 -%169 = OpLabel -OpBranch %167 -%167 = OpLabel -OpBranch %155 -%155 = OpLabel -OpBranch %152 -%153 = OpLabel +%169 = OpCompositeExtract %12 %165 1 +%170 = OpIEqual %91 %169 %13 +%171 = OpSelect %12 %170 %19 %13 +%172 = OpCompositeConstruct %89 %171 %19 +%173 = OpISub %89 %165 %172 +OpStore %163 %173 +OpBranch %161 +%161 = OpLabel +OpSelectionMerge %174 None +OpSwitch %112 %175 1 %175 +%175 = OpLabel +OpSelectionMerge %176 None +OpSwitch %113 %177 +%177 = OpLabel +OpBranch %162 +%176 = OpLabel +OpBranch %174 +%174 = OpLabel +OpBranch %162 +%162 = OpLabel +OpBranch %159 +%160 = OpLabel OpReturn OpFunctionEnd -%176 = OpFunction %2 None %177 -%172 = OpFunctionParameter %3 -%173 = OpFunctionParameter %3 -%174 = OpFunctionParameter %3 -%175 = OpFunctionParameter %3 -%171 = OpLabel -%178 = OpVariable %15 Function %8 -%184 = OpVariable %83 Function %88 -%202 = OpVariable %83 Function %88 -OpBranch %179 -%179 = OpLabel -OpBranch %180 -%180 = OpLabel -OpLoopMerge %181 %183 None -OpBranch %185 -%185 = OpLabel -%186 = OpLoad %82 %184 -%187 = OpIEqual %85 %86 %186 -%188 = OpAll %84 %187 -OpSelectionMerge %189 None -OpBranchConditional %188 %181 %189 -%189 = OpLabel -%190 = OpCompositeExtract %12 %186 1 -%191 = OpIEqual %84 %190 %13 -%192 = OpSelect %12 %191 %19 %13 -%193 = OpCompositeConstruct %82 %192 %19 -%194 = OpISub %82 %186 %193 -OpStore %184 %194 -OpBranch %182 -%182 = OpLabel -OpSelectionMerge %195 None -OpSwitch %172 %197 1 %196 +%183 = OpFunction %2 None %184 +%179 = OpFunctionParameter %3 +%180 = OpFunctionParameter %3 +%181 = OpFunctionParameter %3 +%182 = OpFunctionParameter %3 +%178 = OpLabel +%185 = OpVariable %15 Function %8 +%191 = OpVariable %90 Function %95 +%209 = OpVariable %90 Function %95 +OpBranch %186 +%186 = OpLabel +OpBranch %187 +%187 = OpLabel +OpLoopMerge %188 %190 None +OpBranch %192 +%192 = OpLabel +%193 = OpLoad %89 %191 +%194 = OpIEqual %92 %93 %193 +%195 = OpAll %91 %194 +OpSelectionMerge %196 None +OpBranchConditional %195 %188 %196 %196 = OpLabel -OpStore %178 %7 -OpBranch %195 -%197 = OpLabel -OpBranch %195 -%195 = OpLabel -OpBranch %183 -%183 = OpLabel -OpBranch %180 -%181 = OpLabel -OpBranch %198 -%198 = OpLabel -OpLoopMerge %199 %201 None -OpBranch %203 +%197 = OpCompositeExtract %12 %193 1 +%198 = OpIEqual %91 %197 %13 +%199 = OpSelect %12 %198 %19 %13 +%200 = OpCompositeConstruct %89 %199 %19 +%201 = OpISub %89 %193 %200 +OpStore %191 %201 +OpBranch %189 +%189 = OpLabel +OpSelectionMerge %202 None +OpSwitch %179 %204 1 %203 %203 = OpLabel -%204 = OpLoad %82 %202 -%205 = OpIEqual %85 %86 %204 -%206 = OpAll %84 %205 -OpSelectionMerge %207 None -OpBranchConditional %206 %199 %207 -%207 = OpLabel -%208 = OpCompositeExtract %12 %204 1 -%209 = OpIEqual %84 %208 %13 -%210 = OpSelect %12 %209 %19 %13 -%211 = OpCompositeConstruct %82 %210 %19 -%212 = OpISub %82 %204 %211 -OpStore %202 %212 -OpBranch %200 -%200 = OpLabel -OpSelectionMerge %213 None -OpSwitch %172 %216 1 %214 2 %215 +OpStore %185 %7 +OpBranch %202 +%204 = OpLabel +OpBranch %202 +%202 = OpLabel +OpBranch %190 +%190 = OpLabel +OpBranch %187 +%188 = OpLabel +OpBranch %205 +%205 = OpLabel +OpLoopMerge %206 %208 None +OpBranch %210 +%210 = OpLabel +%211 = OpLoad %89 %209 +%212 = OpIEqual %92 %93 %211 +%213 = OpAll %91 %212 +OpSelectionMerge %214 None +OpBranchConditional %213 %206 %214 %214 = OpLabel -OpBranch %213 -%215 = OpLabel -OpSelectionMerge %217 None -OpSwitch %173 %219 1 %218 -%218 = OpLabel -OpBranch %201 -%219 = OpLabel +%215 = OpCompositeExtract %12 %211 1 +%216 = OpIEqual %91 %215 %13 +%217 = OpSelect %12 %216 %19 %13 +%218 = OpCompositeConstruct %89 %217 %19 +%219 = OpISub %89 %211 %218 +OpStore %209 %219 +OpBranch %207 +%207 = OpLabel OpSelectionMerge %220 None -OpSwitch %174 %222 1 %221 +OpSwitch %179 %223 1 %221 2 %222 %221 = OpLabel -OpStore %178 %9 OpBranch %220 %222 = OpLabel +OpSelectionMerge %224 None +OpSwitch %180 %226 1 %225 +%225 = OpLabel +OpBranch %208 +%226 = OpLabel +OpSelectionMerge %227 None +OpSwitch %181 %229 1 %228 +%228 = OpLabel +OpStore %185 %9 +OpBranch %227 +%229 = OpLabel +OpBranch %227 +%227 = OpLabel +OpBranch %224 +%224 = OpLabel +OpBranch %220 +%223 = OpLabel OpBranch %220 %220 = OpLabel -OpBranch %217 -%217 = OpLabel -OpBranch %213 -%216 = OpLabel -OpBranch %213 -%213 = OpLabel -OpBranch %201 -%201 = OpLabel -OpBranch %198 -%199 = OpLabel +OpBranch %208 +%208 = OpLabel +OpBranch %205 +%206 = OpLabel OpReturn OpFunctionEnd -%224 = OpFunction %2 None %6 -%223 = OpLabel -OpBranch %225 -%225 = OpLabel -%226 = OpFunctionCall %2 %5 -%227 = OpFunctionCall %2 %44 %7 -%228 = OpFunctionCall %2 %50 -%229 = OpFunctionCall %2 %56 -%230 = OpFunctionCall %2 %65 -%231 = OpFunctionCall %2 %76 %7 -%232 = OpFunctionCall %2 %107 %7 %9 %10 -%233 = OpFunctionCall %2 %176 %7 %9 %10 %11 +%231 = OpFunction %2 None %6 +%230 = OpLabel +OpBranch %232 +%232 = OpLabel +%233 = OpFunctionCall %2 %5 +%234 = OpFunctionCall %2 %51 %7 +%235 = OpFunctionCall %2 %57 +%236 = OpFunctionCall %2 %63 +%237 = OpFunctionCall %2 %72 +%238 = OpFunctionCall %2 %83 %7 +%239 = OpFunctionCall %2 %114 %7 %9 %10 +%240 = OpFunctionCall %2 %183 %7 %9 %10 %11 OpReturn OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/spv/wgsl-conversion-float-to-int.spvasm b/naga/tests/out/spv/wgsl-conversion-float-to-int.spvasm index 10cbbe3b8c4..8d8168bfc00 100644 --- a/naga/tests/out/spv/wgsl-conversion-float-to-int.spvasm +++ b/naga/tests/out/spv/wgsl-conversion-float-to-int.spvasm @@ -29,12 +29,12 @@ OpExecutionMode %278 LocalSize 1 1 1 %14 = OpTypeVector %9 2 %15 = OpTypeVector %4 2 %16 = OpTypeVector %5 2 -%17 = OpConstant %3 9.0399e-41 -%18 = OpConstant %3 4.4481e-41 -%19 = OpConstant %4 -3.4028235e38 -%20 = OpConstant %4 3.4028235e38 -%21 = OpConstant %5 -1.7976931348623157e308 -%22 = OpConstant %5 1.7976931348623157e308 +%17 = OpConstant %3 0.000000000000000000000000000000000000000090399 +%18 = OpConstant %3 0.000000000000000000000000000000000000000044481 +%19 = OpConstant %4 -340282350000000000000000000000000000000 +%20 = OpConstant %4 340282350000000000000000000000000000000 +%21 = OpConstant %5 -179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +%22 = OpConstant %5 179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %25 = OpTypeFunction %2 %26 = OpConstant %6 -65504 %27 = OpConstant %6 65504 @@ -60,31 +60,31 @@ OpExecutionMode %278 LocalSize 1 1 1 %54 = OpTypePointer Function %9 %80 = OpTypeFunction %6 %3 %87 = OpTypeFunction %7 %3 -%89 = OpConstant %3 0.0 +%89 = OpConstant %3 0 %95 = OpTypeFunction %8 %3 %102 = OpTypeFunction %9 %3 %109 = OpTypeFunction %6 %4 -%111 = OpConstant %4 -2147483600.0 -%112 = OpConstant %4 2147483500.0 +%111 = OpConstant %4 -2147483600 +%112 = OpConstant %4 2147483500 %118 = OpTypeFunction %7 %4 -%120 = OpConstant %4 0.0 -%121 = OpConstant %4 4294967000.0 +%120 = OpConstant %4 0 +%121 = OpConstant %4 4294967000 %127 = OpTypeFunction %8 %4 -%129 = OpConstant %4 -9.223372e18 -%130 = OpConstant %4 9.2233715e18 +%129 = OpConstant %4 -9223372000000000000 +%130 = OpConstant %4 9223371500000000000 %136 = OpTypeFunction %9 %4 -%138 = OpConstant %4 1.8446743e19 +%138 = OpConstant %4 18446743000000000000 %144 = OpTypeFunction %6 %5 -%146 = OpConstant %5 -2147483648.0 -%147 = OpConstant %5 2147483647.0 +%146 = OpConstant %5 -2147483648 +%147 = OpConstant %5 2147483647 %153 = OpTypeFunction %7 %5 -%155 = OpConstant %5 0.0 -%156 = OpConstant %5 4294967295.0 +%155 = OpConstant %5 0 +%156 = OpConstant %5 4294967295 %162 = OpTypeFunction %8 %5 -%164 = OpConstant %5 -9.223372036854776e18 -%165 = OpConstant %5 9.223372036854775e18 +%164 = OpConstant %5 -9223372036854776000 +%165 = OpConstant %5 9223372036854775000 %171 = OpTypeFunction %9 %5 -%173 = OpConstant %5 1.844674407370955e19 +%173 = OpConstant %5 18446744073709550000 %179 = OpTypeFunction %11 %10 %181 = OpConstantComposite %10 %17 %17 %182 = OpConstantComposite %10 %18 %18 @@ -114,14 +114,14 @@ OpExecutionMode %278 LocalSize 1 1 1 %266 = OpConstantComposite %16 %165 %165 %272 = OpTypeFunction %14 %16 %274 = OpConstantComposite %16 %173 %173 -%279 = OpConstant %3 2.1524e-41 -%280 = OpConstant %4 1.0 -%281 = OpConstant %5 1.0 -%282 = OpConstant %3 2.2959e-41 +%279 = OpConstant %3 0.000000000000000000000000000000000000000021524 +%280 = OpConstant %4 1 +%281 = OpConstant %5 1 +%282 = OpConstant %3 0.000000000000000000000000000000000000000022959 %283 = OpConstantComposite %10 %279 %282 -%284 = OpConstant %4 2.0 +%284 = OpConstant %4 2 %285 = OpConstantComposite %15 %280 %284 -%286 = OpConstant %5 2.0 +%286 = OpConstant %5 2 %287 = OpConstantComposite %16 %281 %286 %24 = OpFunction %2 None %25 %23 = OpLabel diff --git a/naga/tests/out/spv/wgsl-conversions.spvasm b/naga/tests/out/spv/wgsl-conversions.spvasm index 5dae778e115..112979819ce 100644 --- a/naga/tests/out/spv/wgsl-conversions.spvasm +++ b/naga/tests/out/spv/wgsl-conversions.spvasm @@ -10,7 +10,7 @@ OpExecutionMode %8 LocalSize 1 1 1 %2 = OpTypeVoid %4 = OpTypeFloat 32 %3 = OpTypeVector %4 2 -%5 = OpConstant %4 2.0 +%5 = OpConstant %4 2 %6 = OpConstantComposite %3 %5 %5 %9 = OpTypeFunction %2 %11 = OpTypePointer Function %3 diff --git a/naga/tests/out/spv/wgsl-cross.spvasm b/naga/tests/out/spv/wgsl-cross.spvasm index fd13684894b..849b26ef778 100644 --- a/naga/tests/out/spv/wgsl-cross.spvasm +++ b/naga/tests/out/spv/wgsl-cross.spvasm @@ -11,8 +11,8 @@ OpExecutionMode %6 LocalSize 1 1 1 %4 = OpTypeFloat 32 %3 = OpTypeVector %4 3 %7 = OpTypeFunction %2 -%8 = OpConstant %4 0.0 -%9 = OpConstant %4 1.0 +%8 = OpConstant %4 0 +%9 = OpConstant %4 1 %10 = OpConstantComposite %3 %8 %8 %9 %6 = OpFunction %2 None %7 %5 = OpLabel diff --git a/naga/tests/out/spv/wgsl-debug-symbol-large-source.spvasm b/naga/tests/out/spv/wgsl-debug-symbol-large-source.spvasm index f1db8e611ed..ef1d8bc534f 100644 --- a/naga/tests/out/spv/wgsl-debug-symbol-large-source.spvasm +++ b/naga/tests/out/spv/wgsl-debug-symbol-large-source.spvasm @@ -7716,10 +7716,10 @@ OpDecorate %614 Location 0 %49 = OpVariable %46 UniformConstant %50 = OpVariable %48 UniformConstant %54 = OpTypeFunction %5 %5 -%55 = OpConstant %4 34.0 -%56 = OpConstant %4 1.0 +%55 = OpConstant %4 34 +%56 = OpConstant %4 1 %57 = OpConstantComposite %5 %56 %56 %56 -%58 = OpConstant %4 289.0 +%58 = OpConstant %4 289 %59 = OpConstantComposite %5 %58 %58 %58 %68 = OpTypeFunction %4 %6 %69 = OpConstant %4 0.21132487 @@ -7727,18 +7727,18 @@ OpDecorate %614 Location 0 %71 = OpConstant %4 -0.57735026 %72 = OpConstant %4 0.024390243 %73 = OpConstantComposite %7 %69 %70 %71 %72 -%74 = OpConstant %4 0.0 +%74 = OpConstant %4 0 %75 = OpConstantComposite %6 %56 %74 %76 = OpConstantComposite %6 %74 %56 %77 = OpConstantComposite %6 %58 %58 %78 = OpConstant %4 0.5 %79 = OpConstantComposite %5 %78 %78 %78 %80 = OpConstantComposite %5 %74 %74 %74 -%81 = OpConstant %4 2.0 +%81 = OpConstant %4 2 %82 = OpConstant %4 0.85373473 %83 = OpConstant %4 1.7928429 %84 = OpConstantComposite %5 %83 %83 %83 -%85 = OpConstant %4 130.0 +%85 = OpConstant %4 130 %87 = OpTypePointer Function %6 %88 = OpConstantNull %6 %90 = OpConstantNull %6 @@ -7753,7 +7753,7 @@ OpDecorate %614 Location 0 %135 = OpConstant %8 0 %205 = OpConstant %8 5 %206 = OpConstant %4 0.01 -%207 = OpConstant %4 100.0 +%207 = OpConstant %4 100 %208 = OpConstantComposite %6 %207 %207 %209 = OpConstant %4 0.87758255 %210 = OpConstant %4 0.47942555 @@ -7775,11 +7775,11 @@ OpDecorate %614 Location 0 %314 = OpTypeFunction %8 %8 %8 %326 = OpTypeFunction %6 %8 %10 %11 %343 = OpTypeFunction %5 %6 -%344 = OpConstant %4 23.0 -%345 = OpConstant %4 32.0 +%344 = OpConstant %4 23 +%345 = OpConstant %4 32 %346 = OpConstantComposite %6 %344 %345 -%347 = OpConstant %4 -43.0 -%348 = OpConstant %4 3.0 +%347 = OpConstant %4 -43 +%348 = OpConstant %4 3 %349 = OpConstantComposite %6 %347 %348 %365 = OpTypePointer Input %19 %364 = OpVariable %365 Input @@ -7806,9 +7806,9 @@ OpDecorate %614 Location 0 %443 = OpTypePointer Output %6 %442 = OpVariable %443 Output %445 = OpTypePointer Uniform %20 -%447 = OpConstant %4 -1.0 +%447 = OpConstant %4 -1 %448 = OpConstantComposite %6 %447 %447 -%473 = OpConstant %4 4294967000.0 +%473 = OpConstant %4 4294967000 %485 = OpVariable %436 Input %488 = OpTypePointer Input %7 %487 = OpVariable %488 Input @@ -7816,7 +7816,7 @@ OpDecorate %614 Location 0 %490 = OpVariable %491 Input %493 = OpVariable %439 Output %494 = OpVariable %439 Output -%497 = OpConstant %4 6.0 +%497 = OpConstant %4 6 %582 = OpTypePointer Input %5 %581 = OpVariable %582 Input %584 = OpVariable %582 Input diff --git a/naga/tests/out/spv/wgsl-debug-symbol-simple.spvasm b/naga/tests/out/spv/wgsl-debug-symbol-simple.spvasm index bde02c231de..4adb3411b35 100644 --- a/naga/tests/out/spv/wgsl-debug-symbol-simple.spvasm +++ b/naga/tests/out/spv/wgsl-debug-symbol-simple.spvasm @@ -87,7 +87,7 @@ OpDecorate %48 Location 0 %20 = OpTypePointer Output %5 %19 = OpVariable %20 Output %22 = OpTypeFunction %2 -%23 = OpConstant %4 1.0 +%23 = OpConstant %4 1 %25 = OpTypePointer Function %8 %26 = OpConstantNull %8 %28 = OpTypePointer Function %5 diff --git a/naga/tests/out/spv/wgsl-debug-symbol-terrain.spvasm b/naga/tests/out/spv/wgsl-debug-symbol-terrain.spvasm index 69f08bcdb62..47009186afb 100644 --- a/naga/tests/out/spv/wgsl-debug-symbol-terrain.spvasm +++ b/naga/tests/out/spv/wgsl-debug-symbol-terrain.spvasm @@ -551,10 +551,10 @@ OpDecorate %614 Location 0 %49 = OpVariable %46 UniformConstant %50 = OpVariable %48 UniformConstant %54 = OpTypeFunction %5 %5 -%55 = OpConstant %4 34.0 -%56 = OpConstant %4 1.0 +%55 = OpConstant %4 34 +%56 = OpConstant %4 1 %57 = OpConstantComposite %5 %56 %56 %56 -%58 = OpConstant %4 289.0 +%58 = OpConstant %4 289 %59 = OpConstantComposite %5 %58 %58 %58 %68 = OpTypeFunction %4 %6 %69 = OpConstant %4 0.21132487 @@ -562,18 +562,18 @@ OpDecorate %614 Location 0 %71 = OpConstant %4 -0.57735026 %72 = OpConstant %4 0.024390243 %73 = OpConstantComposite %7 %69 %70 %71 %72 -%74 = OpConstant %4 0.0 +%74 = OpConstant %4 0 %75 = OpConstantComposite %6 %56 %74 %76 = OpConstantComposite %6 %74 %56 %77 = OpConstantComposite %6 %58 %58 %78 = OpConstant %4 0.5 %79 = OpConstantComposite %5 %78 %78 %78 %80 = OpConstantComposite %5 %74 %74 %74 -%81 = OpConstant %4 2.0 +%81 = OpConstant %4 2 %82 = OpConstant %4 0.85373473 %83 = OpConstant %4 1.7928429 %84 = OpConstantComposite %5 %83 %83 %83 -%85 = OpConstant %4 130.0 +%85 = OpConstant %4 130 %87 = OpTypePointer Function %6 %88 = OpConstantNull %6 %90 = OpConstantNull %6 @@ -588,7 +588,7 @@ OpDecorate %614 Location 0 %135 = OpConstant %8 0 %205 = OpConstant %8 5 %206 = OpConstant %4 0.01 -%207 = OpConstant %4 100.0 +%207 = OpConstant %4 100 %208 = OpConstantComposite %6 %207 %207 %209 = OpConstant %4 0.87758255 %210 = OpConstant %4 0.47942555 @@ -610,11 +610,11 @@ OpDecorate %614 Location 0 %314 = OpTypeFunction %8 %8 %8 %326 = OpTypeFunction %6 %8 %10 %11 %343 = OpTypeFunction %5 %6 -%344 = OpConstant %4 23.0 -%345 = OpConstant %4 32.0 +%344 = OpConstant %4 23 +%345 = OpConstant %4 32 %346 = OpConstantComposite %6 %344 %345 -%347 = OpConstant %4 -43.0 -%348 = OpConstant %4 3.0 +%347 = OpConstant %4 -43 +%348 = OpConstant %4 3 %349 = OpConstantComposite %6 %347 %348 %365 = OpTypePointer Input %19 %364 = OpVariable %365 Input @@ -641,9 +641,9 @@ OpDecorate %614 Location 0 %443 = OpTypePointer Output %6 %442 = OpVariable %443 Output %445 = OpTypePointer Uniform %20 -%447 = OpConstant %4 -1.0 +%447 = OpConstant %4 -1 %448 = OpConstantComposite %6 %447 %447 -%473 = OpConstant %4 4294967000.0 +%473 = OpConstant %4 4294967000 %485 = OpVariable %436 Input %488 = OpTypePointer Input %7 %487 = OpVariable %488 Input @@ -651,7 +651,7 @@ OpDecorate %614 Location 0 %490 = OpVariable %491 Input %493 = OpVariable %439 Output %494 = OpVariable %439 Output -%497 = OpConstant %4 6.0 +%497 = OpConstant %4 6 %582 = OpTypePointer Input %5 %581 = OpVariable %582 Input %584 = OpVariable %582 Input diff --git a/naga/tests/out/spv/wgsl-empty-if.spvasm b/naga/tests/out/spv/wgsl-empty-if.spvasm new file mode 100644 index 00000000000..3845afcede7 --- /dev/null +++ b/naga/tests/out/spv/wgsl-empty-if.spvasm @@ -0,0 +1,29 @@ +; SPIR-V +; Version: 1.6 +; Generator: rspirv +; Bound: 18 +OpCapability Shader +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint GLCompute %9 "comp" %6 +OpExecutionMode %9 LocalSize 1 1 1 +OpDecorate %6 BuiltIn GlobalInvocationId +%2 = OpTypeVoid +%4 = OpTypeInt 32 0 +%3 = OpTypeVector %4 3 +%7 = OpTypePointer Input %3 +%6 = OpVariable %7 Input +%10 = OpTypeFunction %2 +%11 = OpConstant %4 0 +%12 = OpTypeInt 32 1 +%13 = OpConstant %12 2 +%16 = OpTypeBool +%9 = OpFunction %2 None %10 +%5 = OpLabel +%8 = OpLoad %3 %6 +OpBranch %14 +%14 = OpLabel +%15 = OpCompositeExtract %4 %8 0 +%17 = OpIEqual %16 %15 %11 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/spv/wgsl-extra.spvasm b/naga/tests/out/spv/wgsl-extra.spvasm index 0e84427bad4..0d091ae1fe2 100644 --- a/naga/tests/out/spv/wgsl-extra.spvasm +++ b/naga/tests/out/spv/wgsl-extra.spvasm @@ -37,7 +37,7 @@ OpDecorate %20 Location 0 %23 = OpTypeFunction %2 %24 = OpTypePointer PushConstant %6 %25 = OpConstant %3 0 -%27 = OpConstant %5 1.0 +%27 = OpConstant %5 1 %28 = OpTypeVector %5 3 %29 = OpConstantComposite %28 %27 %27 %27 %32 = OpTypePointer PushConstant %3 diff --git a/naga/tests/out/spv/wgsl-f16-native.spvasm b/naga/tests/out/spv/wgsl-f16-native.spvasm new file mode 100644 index 00000000000..43210270933 --- /dev/null +++ b/naga/tests/out/spv/wgsl-f16-native.spvasm @@ -0,0 +1,761 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 318 +OpCapability Shader +OpCapability Float16 +OpCapability StorageBuffer16BitAccess +OpCapability UniformAndStorageBuffer16BitAccess +OpCapability StorageInputOutput16 +OpExtension "SPV_KHR_16bit_storage" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %54 "test_direct" %14 %17 %20 %23 %26 %29 %32 %35 %38 %40 %42 %44 %46 %48 %50 %52 +OpEntryPoint Fragment %136 "test_struct" %112 %114 %116 %118 %120 %122 %124 %126 %128 %129 %130 %131 %132 %133 %134 %135 +OpEntryPoint Fragment %199 "test_copy_input" %175 %177 %179 %181 %183 %185 %187 %189 %191 %192 %193 %194 %195 %196 %197 %198 +OpEntryPoint Fragment %265 "test_return_partial" %248 %250 %252 %254 %256 %258 %260 %262 %264 +OpEntryPoint Fragment %299 "test_component_access" %275 %277 %279 %281 %283 %285 %287 %289 %291 %292 %293 %294 %295 %296 %297 %298 +OpExecutionMode %54 OriginUpperLeft +OpExecutionMode %136 OriginUpperLeft +OpExecutionMode %199 OriginUpperLeft +OpExecutionMode %265 OriginUpperLeft +OpExecutionMode %299 OriginUpperLeft +%3 = OpString "f16-native.wgsl" +OpSource Unknown 0 %3 "enable f16; + +@fragment +fn test_direct( + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +) -> F16IO { + var output: F16IO; + output.scalar_f16 = scalar_f16 + 1.0h; + output.scalar_f32 = scalar_f32 + 1.0; + output.vec2_f16 = vec2_f16 + vec2(1.0h); + output.vec2_f32 = vec2_f32 + vec2(1.0); + output.vec3_f16 = vec3_f16 + vec3(1.0h); + output.vec3_f32 = vec3_f32 + vec3(1.0); + output.vec4_f16 = vec4_f16 + vec4(1.0h); + output.vec4_f32 = vec4_f32 + vec4(1.0); + return output; +} + +struct F16IO { + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +} + +@fragment +fn test_struct(input: F16IO) -> F16IO { + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_copy_input(input_original: F16IO) -> F16IO { + var input = input_original; + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_return_partial(input_original: F16IO) -> @location(0) f16 { + var input = input_original; + input.scalar_f16 = 0.0h; + return input.scalar_f16; +} + +@fragment +fn test_component_access(input: F16IO) -> F16IO { + var output: F16IO; + output.vec2_f16.x = input.vec2_f16.y; + output.vec2_f16.y = input.vec2_f16.x; + return output; +}" +OpMemberName %12 0 "scalar_f16" +OpMemberName %12 1 "scalar_f32" +OpMemberName %12 2 "vec2_f16" +OpMemberName %12 3 "vec2_f32" +OpMemberName %12 4 "vec3_f16" +OpMemberName %12 5 "vec3_f32" +OpMemberName %12 6 "vec4_f16" +OpMemberName %12 7 "vec4_f32" +OpName %12 "F16IO" +OpName %14 "scalar_f16" +OpName %17 "scalar_f32" +OpName %20 "vec2_f16" +OpName %23 "vec2_f32" +OpName %26 "vec3_f16" +OpName %29 "vec3_f32" +OpName %32 "vec4_f16" +OpName %35 "vec4_f32" +OpName %38 "scalar_f16" +OpName %40 "scalar_f32" +OpName %42 "vec2_f16" +OpName %44 "vec2_f32" +OpName %46 "vec3_f16" +OpName %48 "vec3_f32" +OpName %50 "vec4_f16" +OpName %52 "vec4_f32" +OpName %54 "test_direct" +OpName %64 "output" +OpName %112 "scalar_f16" +OpName %114 "scalar_f32" +OpName %116 "vec2_f16" +OpName %118 "vec2_f32" +OpName %120 "vec3_f16" +OpName %122 "vec3_f32" +OpName %124 "vec4_f16" +OpName %126 "vec4_f32" +OpName %128 "scalar_f16" +OpName %129 "scalar_f32" +OpName %130 "vec2_f16" +OpName %131 "vec2_f32" +OpName %132 "vec3_f16" +OpName %133 "vec3_f32" +OpName %134 "vec4_f16" +OpName %135 "vec4_f32" +OpName %136 "test_struct" +OpName %137 "output" +OpName %175 "scalar_f16" +OpName %177 "scalar_f32" +OpName %179 "vec2_f16" +OpName %181 "vec2_f32" +OpName %183 "vec3_f16" +OpName %185 "vec3_f32" +OpName %187 "vec4_f16" +OpName %189 "vec4_f32" +OpName %191 "scalar_f16" +OpName %192 "scalar_f32" +OpName %193 "vec2_f16" +OpName %194 "vec2_f32" +OpName %195 "vec3_f16" +OpName %196 "vec3_f32" +OpName %197 "vec4_f16" +OpName %198 "vec4_f32" +OpName %199 "test_copy_input" +OpName %200 "input" +OpName %202 "output" +OpName %248 "scalar_f16" +OpName %250 "scalar_f32" +OpName %252 "vec2_f16" +OpName %254 "vec2_f32" +OpName %256 "vec3_f16" +OpName %258 "vec3_f32" +OpName %260 "vec4_f16" +OpName %262 "vec4_f32" +OpName %265 "test_return_partial" +OpName %267 "input" +OpName %275 "scalar_f16" +OpName %277 "scalar_f32" +OpName %279 "vec2_f16" +OpName %281 "vec2_f32" +OpName %283 "vec3_f16" +OpName %285 "vec3_f32" +OpName %287 "vec4_f16" +OpName %289 "vec4_f32" +OpName %291 "scalar_f16" +OpName %292 "scalar_f32" +OpName %293 "vec2_f16" +OpName %294 "vec2_f32" +OpName %295 "vec3_f16" +OpName %296 "vec3_f32" +OpName %297 "vec4_f16" +OpName %298 "vec4_f32" +OpName %299 "test_component_access" +OpName %300 "output" +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 1 Offset 4 +OpMemberDecorate %12 2 Offset 8 +OpMemberDecorate %12 3 Offset 16 +OpMemberDecorate %12 4 Offset 24 +OpMemberDecorate %12 5 Offset 32 +OpMemberDecorate %12 6 Offset 48 +OpMemberDecorate %12 7 Offset 64 +OpDecorate %14 Location 0 +OpDecorate %17 Location 1 +OpDecorate %20 Location 2 +OpDecorate %23 Location 3 +OpDecorate %26 Location 4 +OpDecorate %29 Location 5 +OpDecorate %32 Location 6 +OpDecorate %35 Location 7 +OpDecorate %38 Location 0 +OpDecorate %40 Location 1 +OpDecorate %42 Location 2 +OpDecorate %44 Location 3 +OpDecorate %46 Location 4 +OpDecorate %48 Location 5 +OpDecorate %50 Location 6 +OpDecorate %52 Location 7 +OpDecorate %112 Location 0 +OpDecorate %114 Location 1 +OpDecorate %116 Location 2 +OpDecorate %118 Location 3 +OpDecorate %120 Location 4 +OpDecorate %122 Location 5 +OpDecorate %124 Location 6 +OpDecorate %126 Location 7 +OpDecorate %128 Location 0 +OpDecorate %129 Location 1 +OpDecorate %130 Location 2 +OpDecorate %131 Location 3 +OpDecorate %132 Location 4 +OpDecorate %133 Location 5 +OpDecorate %134 Location 6 +OpDecorate %135 Location 7 +OpDecorate %175 Location 0 +OpDecorate %177 Location 1 +OpDecorate %179 Location 2 +OpDecorate %181 Location 3 +OpDecorate %183 Location 4 +OpDecorate %185 Location 5 +OpDecorate %187 Location 6 +OpDecorate %189 Location 7 +OpDecorate %191 Location 0 +OpDecorate %192 Location 1 +OpDecorate %193 Location 2 +OpDecorate %194 Location 3 +OpDecorate %195 Location 4 +OpDecorate %196 Location 5 +OpDecorate %197 Location 6 +OpDecorate %198 Location 7 +OpDecorate %248 Location 0 +OpDecorate %250 Location 1 +OpDecorate %252 Location 2 +OpDecorate %254 Location 3 +OpDecorate %256 Location 4 +OpDecorate %258 Location 5 +OpDecorate %260 Location 6 +OpDecorate %262 Location 7 +OpDecorate %264 Location 0 +OpDecorate %275 Location 0 +OpDecorate %277 Location 1 +OpDecorate %279 Location 2 +OpDecorate %281 Location 3 +OpDecorate %283 Location 4 +OpDecorate %285 Location 5 +OpDecorate %287 Location 6 +OpDecorate %289 Location 7 +OpDecorate %291 Location 0 +OpDecorate %292 Location 1 +OpDecorate %293 Location 2 +OpDecorate %294 Location 3 +OpDecorate %295 Location 4 +OpDecorate %296 Location 5 +OpDecorate %297 Location 6 +OpDecorate %298 Location 7 +%2 = OpTypeVoid +%4 = OpTypeFloat 16 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %4 2 +%7 = OpTypeVector %5 2 +%8 = OpTypeVector %4 3 +%9 = OpTypeVector %5 3 +%10 = OpTypeVector %4 4 +%11 = OpTypeVector %5 4 +%12 = OpTypeStruct %4 %5 %6 %7 %8 %9 %10 %11 +%15 = OpTypePointer Input %4 +%14 = OpVariable %15 Input +%18 = OpTypePointer Input %5 +%17 = OpVariable %18 Input +%21 = OpTypePointer Input %6 +%20 = OpVariable %21 Input +%24 = OpTypePointer Input %7 +%23 = OpVariable %24 Input +%27 = OpTypePointer Input %8 +%26 = OpVariable %27 Input +%30 = OpTypePointer Input %9 +%29 = OpVariable %30 Input +%33 = OpTypePointer Input %10 +%32 = OpVariable %33 Input +%36 = OpTypePointer Input %11 +%35 = OpVariable %36 Input +%39 = OpTypePointer Output %4 +%38 = OpVariable %39 Output +%41 = OpTypePointer Output %5 +%40 = OpVariable %41 Output +%43 = OpTypePointer Output %6 +%42 = OpVariable %43 Output +%45 = OpTypePointer Output %7 +%44 = OpVariable %45 Output +%47 = OpTypePointer Output %8 +%46 = OpVariable %47 Output +%49 = OpTypePointer Output %9 +%48 = OpVariable %49 Output +%51 = OpTypePointer Output %10 +%50 = OpVariable %51 Output +%53 = OpTypePointer Output %11 +%52 = OpVariable %53 Output +%55 = OpTypeFunction %2 +%56 = OpConstant %4 0.000000000000000000000000000000000000000021524 +%57 = OpConstant %5 1 +%58 = OpConstantComposite %6 %56 %56 +%59 = OpConstantComposite %7 %57 %57 +%60 = OpConstantComposite %8 %56 %56 %56 +%61 = OpConstantComposite %9 %57 %57 %57 +%62 = OpConstantComposite %10 %56 %56 %56 %56 +%63 = OpConstantComposite %11 %57 %57 %57 %57 +%65 = OpTypePointer Function %12 +%66 = OpConstantNull %12 +%68 = OpTypePointer Function %4 +%71 = OpTypeInt 32 0 +%70 = OpConstant %71 0 +%73 = OpTypePointer Function %5 +%75 = OpConstant %71 1 +%77 = OpTypePointer Function %6 +%79 = OpConstant %71 2 +%81 = OpTypePointer Function %7 +%83 = OpConstant %71 3 +%85 = OpTypePointer Function %8 +%87 = OpConstant %71 4 +%89 = OpTypePointer Function %9 +%91 = OpConstant %71 5 +%93 = OpTypePointer Function %10 +%95 = OpConstant %71 6 +%97 = OpTypePointer Function %11 +%99 = OpConstant %71 7 +%112 = OpVariable %15 Input +%114 = OpVariable %18 Input +%116 = OpVariable %21 Input +%118 = OpVariable %24 Input +%120 = OpVariable %27 Input +%122 = OpVariable %30 Input +%124 = OpVariable %33 Input +%126 = OpVariable %36 Input +%128 = OpVariable %39 Output +%129 = OpVariable %41 Output +%130 = OpVariable %43 Output +%131 = OpVariable %45 Output +%132 = OpVariable %47 Output +%133 = OpVariable %49 Output +%134 = OpVariable %51 Output +%135 = OpVariable %53 Output +%138 = OpConstantNull %12 +%175 = OpVariable %15 Input +%177 = OpVariable %18 Input +%179 = OpVariable %21 Input +%181 = OpVariable %24 Input +%183 = OpVariable %27 Input +%185 = OpVariable %30 Input +%187 = OpVariable %33 Input +%189 = OpVariable %36 Input +%191 = OpVariable %39 Output +%192 = OpVariable %41 Output +%193 = OpVariable %43 Output +%194 = OpVariable %45 Output +%195 = OpVariable %47 Output +%196 = OpVariable %49 Output +%197 = OpVariable %51 Output +%198 = OpVariable %53 Output +%201 = OpConstantNull %12 +%203 = OpConstantNull %12 +%248 = OpVariable %15 Input +%250 = OpVariable %18 Input +%252 = OpVariable %21 Input +%254 = OpVariable %24 Input +%256 = OpVariable %27 Input +%258 = OpVariable %30 Input +%260 = OpVariable %33 Input +%262 = OpVariable %36 Input +%264 = OpVariable %39 Output +%266 = OpConstant %4 0 +%268 = OpConstantNull %12 +%275 = OpVariable %15 Input +%277 = OpVariable %18 Input +%279 = OpVariable %21 Input +%281 = OpVariable %24 Input +%283 = OpVariable %27 Input +%285 = OpVariable %30 Input +%287 = OpVariable %33 Input +%289 = OpVariable %36 Input +%291 = OpVariable %39 Output +%292 = OpVariable %41 Output +%293 = OpVariable %43 Output +%294 = OpVariable %45 Output +%295 = OpVariable %47 Output +%296 = OpVariable %49 Output +%297 = OpVariable %51 Output +%298 = OpVariable %53 Output +%301 = OpConstantNull %12 +%54 = OpFunction %2 None %55 +%13 = OpLabel +%64 = OpVariable %65 Function %66 +%16 = OpLoad %4 %14 +%19 = OpLoad %5 %17 +%22 = OpLoad %6 %20 +%25 = OpLoad %7 %23 +%28 = OpLoad %8 %26 +%31 = OpLoad %9 %29 +%34 = OpLoad %10 %32 +%37 = OpLoad %11 %35 +OpBranch %67 +%67 = OpLabel +OpLine %3 15 5 +OpLine %3 15 25 +%69 = OpFAdd %4 %16 %56 +OpLine %3 15 5 +%72 = OpAccessChain %68 %64 %70 +OpStore %72 %69 +OpLine %3 16 5 +OpLine %3 16 25 +%74 = OpFAdd %5 %19 %57 +OpLine %3 16 5 +%76 = OpAccessChain %73 %64 %75 +OpStore %76 %74 +OpLine %3 17 5 +OpLine %3 17 23 +%78 = OpFAdd %6 %22 %58 +OpLine %3 17 5 +%80 = OpAccessChain %77 %64 %79 +OpStore %80 %78 +OpLine %3 18 5 +OpLine %3 18 34 +OpLine %3 18 23 +%82 = OpFAdd %7 %25 %59 +OpLine %3 18 5 +%84 = OpAccessChain %81 %64 %83 +OpStore %84 %82 +OpLine %3 19 5 +OpLine %3 19 23 +%86 = OpFAdd %8 %28 %60 +OpLine %3 19 5 +%88 = OpAccessChain %85 %64 %87 +OpStore %88 %86 +OpLine %3 20 5 +OpLine %3 20 34 +OpLine %3 20 23 +%90 = OpFAdd %9 %31 %61 +OpLine %3 20 5 +%92 = OpAccessChain %89 %64 %91 +OpStore %92 %90 +OpLine %3 21 5 +OpLine %3 21 23 +%94 = OpFAdd %10 %34 %62 +OpLine %3 21 5 +%96 = OpAccessChain %93 %64 %95 +OpStore %96 %94 +OpLine %3 22 5 +OpLine %3 22 34 +OpLine %3 22 23 +%98 = OpFAdd %11 %37 %63 +OpLine %3 22 5 +%100 = OpAccessChain %97 %64 %99 +OpStore %100 %98 +OpLine %3 1 1 +%101 = OpLoad %12 %64 +%102 = OpCompositeExtract %4 %101 0 +OpStore %38 %102 +%103 = OpCompositeExtract %5 %101 1 +OpStore %40 %103 +%104 = OpCompositeExtract %6 %101 2 +OpStore %42 %104 +%105 = OpCompositeExtract %7 %101 3 +OpStore %44 %105 +%106 = OpCompositeExtract %8 %101 4 +OpStore %46 %106 +%107 = OpCompositeExtract %9 %101 5 +OpStore %48 %107 +%108 = OpCompositeExtract %10 %101 6 +OpStore %50 %108 +%109 = OpCompositeExtract %11 %101 7 +OpStore %52 %109 +OpReturn +OpFunctionEnd +%136 = OpFunction %2 None %55 +%110 = OpLabel +%137 = OpVariable %65 Function %138 +%113 = OpLoad %4 %112 +%115 = OpLoad %5 %114 +%117 = OpLoad %6 %116 +%119 = OpLoad %7 %118 +%121 = OpLoad %8 %120 +%123 = OpLoad %9 %122 +%125 = OpLoad %10 %124 +%127 = OpLoad %11 %126 +%111 = OpCompositeConstruct %12 %113 %115 %117 %119 %121 %123 %125 %127 +OpBranch %139 +%139 = OpLabel +OpLine %3 40 5 +%140 = OpCompositeExtract %4 %111 0 +OpLine %3 40 25 +%141 = OpFAdd %4 %140 %56 +OpLine %3 40 5 +%142 = OpAccessChain %68 %137 %70 +OpStore %142 %141 +OpLine %3 41 5 +%143 = OpCompositeExtract %5 %111 1 +OpLine %3 41 25 +%144 = OpFAdd %5 %143 %57 +OpLine %3 41 5 +%145 = OpAccessChain %73 %137 %75 +OpStore %145 %144 +OpLine %3 42 5 +%146 = OpCompositeExtract %6 %111 2 +OpLine %3 42 23 +%147 = OpFAdd %6 %146 %58 +OpLine %3 42 5 +%148 = OpAccessChain %77 %137 %79 +OpStore %148 %147 +OpLine %3 43 5 +%149 = OpCompositeExtract %7 %111 3 +OpLine %3 43 40 +OpLine %3 43 23 +%150 = OpFAdd %7 %149 %59 +OpLine %3 43 5 +%151 = OpAccessChain %81 %137 %83 +OpStore %151 %150 +OpLine %3 44 5 +%152 = OpCompositeExtract %8 %111 4 +OpLine %3 44 23 +%153 = OpFAdd %8 %152 %60 +OpLine %3 44 5 +%154 = OpAccessChain %85 %137 %87 +OpStore %154 %153 +OpLine %3 45 5 +%155 = OpCompositeExtract %9 %111 5 +OpLine %3 45 40 +OpLine %3 45 23 +%156 = OpFAdd %9 %155 %61 +OpLine %3 45 5 +%157 = OpAccessChain %89 %137 %91 +OpStore %157 %156 +OpLine %3 46 5 +%158 = OpCompositeExtract %10 %111 6 +OpLine %3 46 23 +%159 = OpFAdd %10 %158 %62 +OpLine %3 46 5 +%160 = OpAccessChain %93 %137 %95 +OpStore %160 %159 +OpLine %3 47 5 +%161 = OpCompositeExtract %11 %111 7 +OpLine %3 47 40 +OpLine %3 47 23 +%162 = OpFAdd %11 %161 %63 +OpLine %3 47 5 +%163 = OpAccessChain %97 %137 %99 +OpStore %163 %162 +OpLine %3 1 1 +%164 = OpLoad %12 %137 +%165 = OpCompositeExtract %4 %164 0 +OpStore %128 %165 +%166 = OpCompositeExtract %5 %164 1 +OpStore %129 %166 +%167 = OpCompositeExtract %6 %164 2 +OpStore %130 %167 +%168 = OpCompositeExtract %7 %164 3 +OpStore %131 %168 +%169 = OpCompositeExtract %8 %164 4 +OpStore %132 %169 +%170 = OpCompositeExtract %9 %164 5 +OpStore %133 %170 +%171 = OpCompositeExtract %10 %164 6 +OpStore %134 %171 +%172 = OpCompositeExtract %11 %164 7 +OpStore %135 %172 +OpReturn +OpFunctionEnd +%199 = OpFunction %2 None %55 +%173 = OpLabel +%200 = OpVariable %65 Function %201 +%202 = OpVariable %65 Function %203 +%176 = OpLoad %4 %175 +%178 = OpLoad %5 %177 +%180 = OpLoad %6 %179 +%182 = OpLoad %7 %181 +%184 = OpLoad %8 %183 +%186 = OpLoad %9 %185 +%188 = OpLoad %10 %187 +%190 = OpLoad %11 %189 +%174 = OpCompositeConstruct %12 %176 %178 %180 %182 %184 %186 %188 %190 +OpBranch %204 +%204 = OpLabel +OpLine %3 53 5 +OpStore %200 %174 +OpLine %3 55 5 +%205 = OpAccessChain %68 %200 %70 +%206 = OpLoad %4 %205 +OpLine %3 55 25 +%207 = OpFAdd %4 %206 %56 +OpLine %3 55 5 +%208 = OpAccessChain %68 %202 %70 +OpStore %208 %207 +OpLine %3 56 5 +%209 = OpAccessChain %73 %200 %75 +%210 = OpLoad %5 %209 +OpLine %3 56 25 +%211 = OpFAdd %5 %210 %57 +OpLine %3 56 5 +%212 = OpAccessChain %73 %202 %75 +OpStore %212 %211 +OpLine %3 57 5 +%213 = OpAccessChain %77 %200 %79 +%214 = OpLoad %6 %213 +OpLine %3 57 23 +%215 = OpFAdd %6 %214 %58 +OpLine %3 57 5 +%216 = OpAccessChain %77 %202 %79 +OpStore %216 %215 +OpLine %3 58 5 +%217 = OpAccessChain %81 %200 %83 +%218 = OpLoad %7 %217 +OpLine %3 58 40 +OpLine %3 58 23 +%219 = OpFAdd %7 %218 %59 +OpLine %3 58 5 +%220 = OpAccessChain %81 %202 %83 +OpStore %220 %219 +OpLine %3 59 5 +%221 = OpAccessChain %85 %200 %87 +%222 = OpLoad %8 %221 +OpLine %3 59 23 +%223 = OpFAdd %8 %222 %60 +OpLine %3 59 5 +%224 = OpAccessChain %85 %202 %87 +OpStore %224 %223 +OpLine %3 60 5 +%225 = OpAccessChain %89 %200 %91 +%226 = OpLoad %9 %225 +OpLine %3 60 40 +OpLine %3 60 23 +%227 = OpFAdd %9 %226 %61 +OpLine %3 60 5 +%228 = OpAccessChain %89 %202 %91 +OpStore %228 %227 +OpLine %3 61 5 +%229 = OpAccessChain %93 %200 %95 +%230 = OpLoad %10 %229 +OpLine %3 61 23 +%231 = OpFAdd %10 %230 %62 +OpLine %3 61 5 +%232 = OpAccessChain %93 %202 %95 +OpStore %232 %231 +OpLine %3 62 5 +%233 = OpAccessChain %97 %200 %99 +%234 = OpLoad %11 %233 +OpLine %3 62 40 +OpLine %3 62 23 +%235 = OpFAdd %11 %234 %63 +OpLine %3 62 5 +%236 = OpAccessChain %97 %202 %99 +OpStore %236 %235 +OpLine %3 1 1 +%237 = OpLoad %12 %202 +%238 = OpCompositeExtract %4 %237 0 +OpStore %191 %238 +%239 = OpCompositeExtract %5 %237 1 +OpStore %192 %239 +%240 = OpCompositeExtract %6 %237 2 +OpStore %193 %240 +%241 = OpCompositeExtract %7 %237 3 +OpStore %194 %241 +%242 = OpCompositeExtract %8 %237 4 +OpStore %195 %242 +%243 = OpCompositeExtract %9 %237 5 +OpStore %196 %243 +%244 = OpCompositeExtract %10 %237 6 +OpStore %197 %244 +%245 = OpCompositeExtract %11 %237 7 +OpStore %198 %245 +OpReturn +OpFunctionEnd +%265 = OpFunction %2 None %55 +%246 = OpLabel +%267 = OpVariable %65 Function %268 +%249 = OpLoad %4 %248 +%251 = OpLoad %5 %250 +%253 = OpLoad %6 %252 +%255 = OpLoad %7 %254 +%257 = OpLoad %8 %256 +%259 = OpLoad %9 %258 +%261 = OpLoad %10 %260 +%263 = OpLoad %11 %262 +%247 = OpCompositeConstruct %12 %249 %251 %253 %255 %257 %259 %261 %263 +OpBranch %269 +%269 = OpLabel +OpLine %3 68 5 +OpStore %267 %247 +OpLine %3 69 5 +OpLine %3 69 5 +%270 = OpAccessChain %68 %267 %70 +OpStore %270 %266 +OpLine %3 70 12 +%271 = OpAccessChain %68 %267 %70 +%272 = OpLoad %4 %271 +OpStore %264 %272 +OpReturn +OpFunctionEnd +%299 = OpFunction %2 None %55 +%273 = OpLabel +%300 = OpVariable %65 Function %301 +%276 = OpLoad %4 %275 +%278 = OpLoad %5 %277 +%280 = OpLoad %6 %279 +%282 = OpLoad %7 %281 +%284 = OpLoad %8 %283 +%286 = OpLoad %9 %285 +%288 = OpLoad %10 %287 +%290 = OpLoad %11 %289 +%274 = OpCompositeConstruct %12 %276 %278 %280 %282 %284 %286 %288 %290 +OpBranch %302 +%302 = OpLabel +OpLine %3 76 5 +%303 = OpCompositeExtract %6 %274 2 +%304 = OpCompositeExtract %4 %303 1 +OpLine %3 76 5 +%305 = OpAccessChain %68 %300 %79 %70 +OpStore %305 %304 +OpLine %3 77 5 +%306 = OpCompositeExtract %6 %274 2 +%307 = OpCompositeExtract %4 %306 0 +OpLine %3 77 5 +%308 = OpAccessChain %68 %300 %79 %75 +OpStore %308 %307 +OpLine %3 1 1 +%309 = OpLoad %12 %300 +%310 = OpCompositeExtract %4 %309 0 +OpStore %291 %310 +%311 = OpCompositeExtract %5 %309 1 +OpStore %292 %311 +%312 = OpCompositeExtract %6 %309 2 +OpStore %293 %312 +%313 = OpCompositeExtract %7 %309 3 +OpStore %294 %313 +%314 = OpCompositeExtract %8 %309 4 +OpStore %295 %314 +%315 = OpCompositeExtract %9 %309 5 +OpStore %296 %315 +%316 = OpCompositeExtract %10 %309 6 +OpStore %297 %316 +%317 = OpCompositeExtract %11 %309 7 +OpStore %298 %317 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/spv/wgsl-f16-polyfill.spvasm b/naga/tests/out/spv/wgsl-f16-polyfill.spvasm new file mode 100644 index 00000000000..d673816a486 --- /dev/null +++ b/naga/tests/out/spv/wgsl-f16-polyfill.spvasm @@ -0,0 +1,789 @@ +; SPIR-V +; Version: 1.1 +; Generator: rspirv +; Bound: 347 +OpCapability Shader +OpCapability Float16 +OpCapability StorageBuffer16BitAccess +OpCapability UniformAndStorageBuffer16BitAccess +OpExtension "SPV_KHR_16bit_storage" +%1 = OpExtInstImport "GLSL.std.450" +OpMemoryModel Logical GLSL450 +OpEntryPoint Fragment %50 "test_direct" %14 %18 %20 %24 %26 %30 %32 %36 %38 %40 %41 %43 %44 %46 %47 %49 +OpEntryPoint Fragment %140 "test_struct" %112 %115 %117 %120 %122 %125 %127 %130 %132 %133 %134 %135 %136 %137 %138 %139 +OpEntryPoint Fragment %211 "test_copy_input" %183 %186 %188 %191 %193 %196 %198 %201 %203 %204 %205 %206 %207 %208 %209 %210 +OpEntryPoint Fragment %285 "test_return_partial" %264 %267 %269 %272 %274 %277 %279 %282 %284 +OpEntryPoint Fragment %324 "test_component_access" %296 %299 %301 %304 %306 %309 %311 %314 %316 %317 %318 %319 %320 %321 %322 %323 +OpExecutionMode %50 OriginUpperLeft +OpExecutionMode %140 OriginUpperLeft +OpExecutionMode %211 OriginUpperLeft +OpExecutionMode %285 OriginUpperLeft +OpExecutionMode %324 OriginUpperLeft +%3 = OpString "f16-polyfill.wgsl" +OpSource Unknown 0 %3 "enable f16; + +@fragment +fn test_direct( + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +) -> F16IO { + var output: F16IO; + output.scalar_f16 = scalar_f16 + 1.0h; + output.scalar_f32 = scalar_f32 + 1.0; + output.vec2_f16 = vec2_f16 + vec2(1.0h); + output.vec2_f32 = vec2_f32 + vec2(1.0); + output.vec3_f16 = vec3_f16 + vec3(1.0h); + output.vec3_f32 = vec3_f32 + vec3(1.0); + output.vec4_f16 = vec4_f16 + vec4(1.0h); + output.vec4_f32 = vec4_f32 + vec4(1.0); + return output; +} + +struct F16IO { + @location(0) scalar_f16: f16, + @location(1) scalar_f32: f32, + @location(2) vec2_f16: vec2, + @location(3) vec2_f32: vec2, + @location(4) vec3_f16: vec3, + @location(5) vec3_f32: vec3, + @location(6) vec4_f16: vec4, + @location(7) vec4_f32: vec4, +} + +@fragment +fn test_struct(input: F16IO) -> F16IO { + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_copy_input(input_original: F16IO) -> F16IO { + var input = input_original; + var output: F16IO; + output.scalar_f16 = input.scalar_f16 + 1.0h; + output.scalar_f32 = input.scalar_f32 + 1.0; + output.vec2_f16 = input.vec2_f16 + vec2(1.0h); + output.vec2_f32 = input.vec2_f32 + vec2(1.0); + output.vec3_f16 = input.vec3_f16 + vec3(1.0h); + output.vec3_f32 = input.vec3_f32 + vec3(1.0); + output.vec4_f16 = input.vec4_f16 + vec4(1.0h); + output.vec4_f32 = input.vec4_f32 + vec4(1.0); + return output; +} + +@fragment +fn test_return_partial(input_original: F16IO) -> @location(0) f16 { + var input = input_original; + input.scalar_f16 = 0.0h; + return input.scalar_f16; +} + +@fragment +fn test_component_access(input: F16IO) -> F16IO { + var output: F16IO; + output.vec2_f16.x = input.vec2_f16.y; + output.vec2_f16.y = input.vec2_f16.x; + return output; +}" +OpMemberName %12 0 "scalar_f16" +OpMemberName %12 1 "scalar_f32" +OpMemberName %12 2 "vec2_f16" +OpMemberName %12 3 "vec2_f32" +OpMemberName %12 4 "vec3_f16" +OpMemberName %12 5 "vec3_f32" +OpMemberName %12 6 "vec4_f16" +OpMemberName %12 7 "vec4_f32" +OpName %12 "F16IO" +OpName %14 "scalar_f16" +OpName %18 "scalar_f32" +OpName %20 "vec2_f16" +OpName %24 "vec2_f32" +OpName %26 "vec3_f16" +OpName %30 "vec3_f32" +OpName %32 "vec4_f16" +OpName %36 "vec4_f32" +OpName %38 "scalar_f16" +OpName %40 "scalar_f32" +OpName %41 "vec2_f16" +OpName %43 "vec2_f32" +OpName %44 "vec3_f16" +OpName %46 "vec3_f32" +OpName %47 "vec4_f16" +OpName %49 "vec4_f32" +OpName %50 "test_direct" +OpName %60 "output" +OpName %112 "scalar_f16" +OpName %115 "scalar_f32" +OpName %117 "vec2_f16" +OpName %120 "vec2_f32" +OpName %122 "vec3_f16" +OpName %125 "vec3_f32" +OpName %127 "vec4_f16" +OpName %130 "vec4_f32" +OpName %132 "scalar_f16" +OpName %133 "scalar_f32" +OpName %134 "vec2_f16" +OpName %135 "vec2_f32" +OpName %136 "vec3_f16" +OpName %137 "vec3_f32" +OpName %138 "vec4_f16" +OpName %139 "vec4_f32" +OpName %140 "test_struct" +OpName %141 "output" +OpName %183 "scalar_f16" +OpName %186 "scalar_f32" +OpName %188 "vec2_f16" +OpName %191 "vec2_f32" +OpName %193 "vec3_f16" +OpName %196 "vec3_f32" +OpName %198 "vec4_f16" +OpName %201 "vec4_f32" +OpName %203 "scalar_f16" +OpName %204 "scalar_f32" +OpName %205 "vec2_f16" +OpName %206 "vec2_f32" +OpName %207 "vec3_f16" +OpName %208 "vec3_f32" +OpName %209 "vec4_f16" +OpName %210 "vec4_f32" +OpName %211 "test_copy_input" +OpName %212 "input" +OpName %214 "output" +OpName %264 "scalar_f16" +OpName %267 "scalar_f32" +OpName %269 "vec2_f16" +OpName %272 "vec2_f32" +OpName %274 "vec3_f16" +OpName %277 "vec3_f32" +OpName %279 "vec4_f16" +OpName %282 "vec4_f32" +OpName %285 "test_return_partial" +OpName %287 "input" +OpName %296 "scalar_f16" +OpName %299 "scalar_f32" +OpName %301 "vec2_f16" +OpName %304 "vec2_f32" +OpName %306 "vec3_f16" +OpName %309 "vec3_f32" +OpName %311 "vec4_f16" +OpName %314 "vec4_f32" +OpName %316 "scalar_f16" +OpName %317 "scalar_f32" +OpName %318 "vec2_f16" +OpName %319 "vec2_f32" +OpName %320 "vec3_f16" +OpName %321 "vec3_f32" +OpName %322 "vec4_f16" +OpName %323 "vec4_f32" +OpName %324 "test_component_access" +OpName %325 "output" +OpMemberDecorate %12 0 Offset 0 +OpMemberDecorate %12 1 Offset 4 +OpMemberDecorate %12 2 Offset 8 +OpMemberDecorate %12 3 Offset 16 +OpMemberDecorate %12 4 Offset 24 +OpMemberDecorate %12 5 Offset 32 +OpMemberDecorate %12 6 Offset 48 +OpMemberDecorate %12 7 Offset 64 +OpDecorate %14 Location 0 +OpDecorate %18 Location 1 +OpDecorate %20 Location 2 +OpDecorate %24 Location 3 +OpDecorate %26 Location 4 +OpDecorate %30 Location 5 +OpDecorate %32 Location 6 +OpDecorate %36 Location 7 +OpDecorate %38 Location 0 +OpDecorate %40 Location 1 +OpDecorate %41 Location 2 +OpDecorate %43 Location 3 +OpDecorate %44 Location 4 +OpDecorate %46 Location 5 +OpDecorate %47 Location 6 +OpDecorate %49 Location 7 +OpDecorate %112 Location 0 +OpDecorate %115 Location 1 +OpDecorate %117 Location 2 +OpDecorate %120 Location 3 +OpDecorate %122 Location 4 +OpDecorate %125 Location 5 +OpDecorate %127 Location 6 +OpDecorate %130 Location 7 +OpDecorate %132 Location 0 +OpDecorate %133 Location 1 +OpDecorate %134 Location 2 +OpDecorate %135 Location 3 +OpDecorate %136 Location 4 +OpDecorate %137 Location 5 +OpDecorate %138 Location 6 +OpDecorate %139 Location 7 +OpDecorate %183 Location 0 +OpDecorate %186 Location 1 +OpDecorate %188 Location 2 +OpDecorate %191 Location 3 +OpDecorate %193 Location 4 +OpDecorate %196 Location 5 +OpDecorate %198 Location 6 +OpDecorate %201 Location 7 +OpDecorate %203 Location 0 +OpDecorate %204 Location 1 +OpDecorate %205 Location 2 +OpDecorate %206 Location 3 +OpDecorate %207 Location 4 +OpDecorate %208 Location 5 +OpDecorate %209 Location 6 +OpDecorate %210 Location 7 +OpDecorate %264 Location 0 +OpDecorate %267 Location 1 +OpDecorate %269 Location 2 +OpDecorate %272 Location 3 +OpDecorate %274 Location 4 +OpDecorate %277 Location 5 +OpDecorate %279 Location 6 +OpDecorate %282 Location 7 +OpDecorate %284 Location 0 +OpDecorate %296 Location 0 +OpDecorate %299 Location 1 +OpDecorate %301 Location 2 +OpDecorate %304 Location 3 +OpDecorate %306 Location 4 +OpDecorate %309 Location 5 +OpDecorate %311 Location 6 +OpDecorate %314 Location 7 +OpDecorate %316 Location 0 +OpDecorate %317 Location 1 +OpDecorate %318 Location 2 +OpDecorate %319 Location 3 +OpDecorate %320 Location 4 +OpDecorate %321 Location 5 +OpDecorate %322 Location 6 +OpDecorate %323 Location 7 +%2 = OpTypeVoid +%4 = OpTypeFloat 16 +%5 = OpTypeFloat 32 +%6 = OpTypeVector %4 2 +%7 = OpTypeVector %5 2 +%8 = OpTypeVector %4 3 +%9 = OpTypeVector %5 3 +%10 = OpTypeVector %4 4 +%11 = OpTypeVector %5 4 +%12 = OpTypeStruct %4 %5 %6 %7 %8 %9 %10 %11 +%15 = OpTypePointer Input %5 +%14 = OpVariable %15 Input +%18 = OpVariable %15 Input +%21 = OpTypePointer Input %7 +%20 = OpVariable %21 Input +%24 = OpVariable %21 Input +%27 = OpTypePointer Input %9 +%26 = OpVariable %27 Input +%30 = OpVariable %27 Input +%33 = OpTypePointer Input %11 +%32 = OpVariable %33 Input +%36 = OpVariable %33 Input +%39 = OpTypePointer Output %5 +%38 = OpVariable %39 Output +%40 = OpVariable %39 Output +%42 = OpTypePointer Output %7 +%41 = OpVariable %42 Output +%43 = OpVariable %42 Output +%45 = OpTypePointer Output %9 +%44 = OpVariable %45 Output +%46 = OpVariable %45 Output +%48 = OpTypePointer Output %11 +%47 = OpVariable %48 Output +%49 = OpVariable %48 Output +%51 = OpTypeFunction %2 +%52 = OpConstant %4 0.000000000000000000000000000000000000000021524 +%53 = OpConstant %5 1 +%54 = OpConstantComposite %6 %52 %52 +%55 = OpConstantComposite %7 %53 %53 +%56 = OpConstantComposite %8 %52 %52 %52 +%57 = OpConstantComposite %9 %53 %53 %53 +%58 = OpConstantComposite %10 %52 %52 %52 %52 +%59 = OpConstantComposite %11 %53 %53 %53 %53 +%61 = OpTypePointer Function %12 +%62 = OpConstantNull %12 +%64 = OpTypePointer Function %4 +%67 = OpTypeInt 32 0 +%66 = OpConstant %67 0 +%69 = OpTypePointer Function %5 +%71 = OpConstant %67 1 +%73 = OpTypePointer Function %6 +%75 = OpConstant %67 2 +%77 = OpTypePointer Function %7 +%79 = OpConstant %67 3 +%81 = OpTypePointer Function %8 +%83 = OpConstant %67 4 +%85 = OpTypePointer Function %9 +%87 = OpConstant %67 5 +%89 = OpTypePointer Function %10 +%91 = OpConstant %67 6 +%93 = OpTypePointer Function %11 +%95 = OpConstant %67 7 +%112 = OpVariable %15 Input +%115 = OpVariable %15 Input +%117 = OpVariable %21 Input +%120 = OpVariable %21 Input +%122 = OpVariable %27 Input +%125 = OpVariable %27 Input +%127 = OpVariable %33 Input +%130 = OpVariable %33 Input +%132 = OpVariable %39 Output +%133 = OpVariable %39 Output +%134 = OpVariable %42 Output +%135 = OpVariable %42 Output +%136 = OpVariable %45 Output +%137 = OpVariable %45 Output +%138 = OpVariable %48 Output +%139 = OpVariable %48 Output +%142 = OpConstantNull %12 +%183 = OpVariable %15 Input +%186 = OpVariable %15 Input +%188 = OpVariable %21 Input +%191 = OpVariable %21 Input +%193 = OpVariable %27 Input +%196 = OpVariable %27 Input +%198 = OpVariable %33 Input +%201 = OpVariable %33 Input +%203 = OpVariable %39 Output +%204 = OpVariable %39 Output +%205 = OpVariable %42 Output +%206 = OpVariable %42 Output +%207 = OpVariable %45 Output +%208 = OpVariable %45 Output +%209 = OpVariable %48 Output +%210 = OpVariable %48 Output +%213 = OpConstantNull %12 +%215 = OpConstantNull %12 +%264 = OpVariable %15 Input +%267 = OpVariable %15 Input +%269 = OpVariable %21 Input +%272 = OpVariable %21 Input +%274 = OpVariable %27 Input +%277 = OpVariable %27 Input +%279 = OpVariable %33 Input +%282 = OpVariable %33 Input +%284 = OpVariable %39 Output +%286 = OpConstant %4 0 +%288 = OpConstantNull %12 +%296 = OpVariable %15 Input +%299 = OpVariable %15 Input +%301 = OpVariable %21 Input +%304 = OpVariable %21 Input +%306 = OpVariable %27 Input +%309 = OpVariable %27 Input +%311 = OpVariable %33 Input +%314 = OpVariable %33 Input +%316 = OpVariable %39 Output +%317 = OpVariable %39 Output +%318 = OpVariable %42 Output +%319 = OpVariable %42 Output +%320 = OpVariable %45 Output +%321 = OpVariable %45 Output +%322 = OpVariable %48 Output +%323 = OpVariable %48 Output +%326 = OpConstantNull %12 +%50 = OpFunction %2 None %51 +%13 = OpLabel +%60 = OpVariable %61 Function %62 +%16 = OpLoad %5 %14 +%17 = OpFConvert %4 %16 +%19 = OpLoad %5 %18 +%22 = OpLoad %7 %20 +%23 = OpFConvert %6 %22 +%25 = OpLoad %7 %24 +%28 = OpLoad %9 %26 +%29 = OpFConvert %8 %28 +%31 = OpLoad %9 %30 +%34 = OpLoad %11 %32 +%35 = OpFConvert %10 %34 +%37 = OpLoad %11 %36 +OpBranch %63 +%63 = OpLabel +OpLine %3 15 5 +OpLine %3 15 25 +%65 = OpFAdd %4 %17 %52 +OpLine %3 15 5 +%68 = OpAccessChain %64 %60 %66 +OpStore %68 %65 +OpLine %3 16 5 +OpLine %3 16 25 +%70 = OpFAdd %5 %19 %53 +OpLine %3 16 5 +%72 = OpAccessChain %69 %60 %71 +OpStore %72 %70 +OpLine %3 17 5 +OpLine %3 17 23 +%74 = OpFAdd %6 %23 %54 +OpLine %3 17 5 +%76 = OpAccessChain %73 %60 %75 +OpStore %76 %74 +OpLine %3 18 5 +OpLine %3 18 34 +OpLine %3 18 23 +%78 = OpFAdd %7 %25 %55 +OpLine %3 18 5 +%80 = OpAccessChain %77 %60 %79 +OpStore %80 %78 +OpLine %3 19 5 +OpLine %3 19 23 +%82 = OpFAdd %8 %29 %56 +OpLine %3 19 5 +%84 = OpAccessChain %81 %60 %83 +OpStore %84 %82 +OpLine %3 20 5 +OpLine %3 20 34 +OpLine %3 20 23 +%86 = OpFAdd %9 %31 %57 +OpLine %3 20 5 +%88 = OpAccessChain %85 %60 %87 +OpStore %88 %86 +OpLine %3 21 5 +OpLine %3 21 23 +%90 = OpFAdd %10 %35 %58 +OpLine %3 21 5 +%92 = OpAccessChain %89 %60 %91 +OpStore %92 %90 +OpLine %3 22 5 +OpLine %3 22 34 +OpLine %3 22 23 +%94 = OpFAdd %11 %37 %59 +OpLine %3 22 5 +%96 = OpAccessChain %93 %60 %95 +OpStore %96 %94 +OpLine %3 1 1 +%97 = OpLoad %12 %60 +%98 = OpCompositeExtract %4 %97 0 +%99 = OpFConvert %5 %98 +OpStore %38 %99 +%100 = OpCompositeExtract %5 %97 1 +OpStore %40 %100 +%101 = OpCompositeExtract %6 %97 2 +%102 = OpFConvert %7 %101 +OpStore %41 %102 +%103 = OpCompositeExtract %7 %97 3 +OpStore %43 %103 +%104 = OpCompositeExtract %8 %97 4 +%105 = OpFConvert %9 %104 +OpStore %44 %105 +%106 = OpCompositeExtract %9 %97 5 +OpStore %46 %106 +%107 = OpCompositeExtract %10 %97 6 +%108 = OpFConvert %11 %107 +OpStore %47 %108 +%109 = OpCompositeExtract %11 %97 7 +OpStore %49 %109 +OpReturn +OpFunctionEnd +%140 = OpFunction %2 None %51 +%110 = OpLabel +%141 = OpVariable %61 Function %142 +%113 = OpLoad %5 %112 +%114 = OpFConvert %4 %113 +%116 = OpLoad %5 %115 +%118 = OpLoad %7 %117 +%119 = OpFConvert %6 %118 +%121 = OpLoad %7 %120 +%123 = OpLoad %9 %122 +%124 = OpFConvert %8 %123 +%126 = OpLoad %9 %125 +%128 = OpLoad %11 %127 +%129 = OpFConvert %10 %128 +%131 = OpLoad %11 %130 +%111 = OpCompositeConstruct %12 %114 %116 %119 %121 %124 %126 %129 %131 +OpBranch %143 +%143 = OpLabel +OpLine %3 40 5 +%144 = OpCompositeExtract %4 %111 0 +OpLine %3 40 25 +%145 = OpFAdd %4 %144 %52 +OpLine %3 40 5 +%146 = OpAccessChain %64 %141 %66 +OpStore %146 %145 +OpLine %3 41 5 +%147 = OpCompositeExtract %5 %111 1 +OpLine %3 41 25 +%148 = OpFAdd %5 %147 %53 +OpLine %3 41 5 +%149 = OpAccessChain %69 %141 %71 +OpStore %149 %148 +OpLine %3 42 5 +%150 = OpCompositeExtract %6 %111 2 +OpLine %3 42 23 +%151 = OpFAdd %6 %150 %54 +OpLine %3 42 5 +%152 = OpAccessChain %73 %141 %75 +OpStore %152 %151 +OpLine %3 43 5 +%153 = OpCompositeExtract %7 %111 3 +OpLine %3 43 40 +OpLine %3 43 23 +%154 = OpFAdd %7 %153 %55 +OpLine %3 43 5 +%155 = OpAccessChain %77 %141 %79 +OpStore %155 %154 +OpLine %3 44 5 +%156 = OpCompositeExtract %8 %111 4 +OpLine %3 44 23 +%157 = OpFAdd %8 %156 %56 +OpLine %3 44 5 +%158 = OpAccessChain %81 %141 %83 +OpStore %158 %157 +OpLine %3 45 5 +%159 = OpCompositeExtract %9 %111 5 +OpLine %3 45 40 +OpLine %3 45 23 +%160 = OpFAdd %9 %159 %57 +OpLine %3 45 5 +%161 = OpAccessChain %85 %141 %87 +OpStore %161 %160 +OpLine %3 46 5 +%162 = OpCompositeExtract %10 %111 6 +OpLine %3 46 23 +%163 = OpFAdd %10 %162 %58 +OpLine %3 46 5 +%164 = OpAccessChain %89 %141 %91 +OpStore %164 %163 +OpLine %3 47 5 +%165 = OpCompositeExtract %11 %111 7 +OpLine %3 47 40 +OpLine %3 47 23 +%166 = OpFAdd %11 %165 %59 +OpLine %3 47 5 +%167 = OpAccessChain %93 %141 %95 +OpStore %167 %166 +OpLine %3 1 1 +%168 = OpLoad %12 %141 +%169 = OpCompositeExtract %4 %168 0 +%170 = OpFConvert %5 %169 +OpStore %132 %170 +%171 = OpCompositeExtract %5 %168 1 +OpStore %133 %171 +%172 = OpCompositeExtract %6 %168 2 +%173 = OpFConvert %7 %172 +OpStore %134 %173 +%174 = OpCompositeExtract %7 %168 3 +OpStore %135 %174 +%175 = OpCompositeExtract %8 %168 4 +%176 = OpFConvert %9 %175 +OpStore %136 %176 +%177 = OpCompositeExtract %9 %168 5 +OpStore %137 %177 +%178 = OpCompositeExtract %10 %168 6 +%179 = OpFConvert %11 %178 +OpStore %138 %179 +%180 = OpCompositeExtract %11 %168 7 +OpStore %139 %180 +OpReturn +OpFunctionEnd +%211 = OpFunction %2 None %51 +%181 = OpLabel +%212 = OpVariable %61 Function %213 +%214 = OpVariable %61 Function %215 +%184 = OpLoad %5 %183 +%185 = OpFConvert %4 %184 +%187 = OpLoad %5 %186 +%189 = OpLoad %7 %188 +%190 = OpFConvert %6 %189 +%192 = OpLoad %7 %191 +%194 = OpLoad %9 %193 +%195 = OpFConvert %8 %194 +%197 = OpLoad %9 %196 +%199 = OpLoad %11 %198 +%200 = OpFConvert %10 %199 +%202 = OpLoad %11 %201 +%182 = OpCompositeConstruct %12 %185 %187 %190 %192 %195 %197 %200 %202 +OpBranch %216 +%216 = OpLabel +OpLine %3 53 5 +OpStore %212 %182 +OpLine %3 55 5 +%217 = OpAccessChain %64 %212 %66 +%218 = OpLoad %4 %217 +OpLine %3 55 25 +%219 = OpFAdd %4 %218 %52 +OpLine %3 55 5 +%220 = OpAccessChain %64 %214 %66 +OpStore %220 %219 +OpLine %3 56 5 +%221 = OpAccessChain %69 %212 %71 +%222 = OpLoad %5 %221 +OpLine %3 56 25 +%223 = OpFAdd %5 %222 %53 +OpLine %3 56 5 +%224 = OpAccessChain %69 %214 %71 +OpStore %224 %223 +OpLine %3 57 5 +%225 = OpAccessChain %73 %212 %75 +%226 = OpLoad %6 %225 +OpLine %3 57 23 +%227 = OpFAdd %6 %226 %54 +OpLine %3 57 5 +%228 = OpAccessChain %73 %214 %75 +OpStore %228 %227 +OpLine %3 58 5 +%229 = OpAccessChain %77 %212 %79 +%230 = OpLoad %7 %229 +OpLine %3 58 40 +OpLine %3 58 23 +%231 = OpFAdd %7 %230 %55 +OpLine %3 58 5 +%232 = OpAccessChain %77 %214 %79 +OpStore %232 %231 +OpLine %3 59 5 +%233 = OpAccessChain %81 %212 %83 +%234 = OpLoad %8 %233 +OpLine %3 59 23 +%235 = OpFAdd %8 %234 %56 +OpLine %3 59 5 +%236 = OpAccessChain %81 %214 %83 +OpStore %236 %235 +OpLine %3 60 5 +%237 = OpAccessChain %85 %212 %87 +%238 = OpLoad %9 %237 +OpLine %3 60 40 +OpLine %3 60 23 +%239 = OpFAdd %9 %238 %57 +OpLine %3 60 5 +%240 = OpAccessChain %85 %214 %87 +OpStore %240 %239 +OpLine %3 61 5 +%241 = OpAccessChain %89 %212 %91 +%242 = OpLoad %10 %241 +OpLine %3 61 23 +%243 = OpFAdd %10 %242 %58 +OpLine %3 61 5 +%244 = OpAccessChain %89 %214 %91 +OpStore %244 %243 +OpLine %3 62 5 +%245 = OpAccessChain %93 %212 %95 +%246 = OpLoad %11 %245 +OpLine %3 62 40 +OpLine %3 62 23 +%247 = OpFAdd %11 %246 %59 +OpLine %3 62 5 +%248 = OpAccessChain %93 %214 %95 +OpStore %248 %247 +OpLine %3 1 1 +%249 = OpLoad %12 %214 +%250 = OpCompositeExtract %4 %249 0 +%251 = OpFConvert %5 %250 +OpStore %203 %251 +%252 = OpCompositeExtract %5 %249 1 +OpStore %204 %252 +%253 = OpCompositeExtract %6 %249 2 +%254 = OpFConvert %7 %253 +OpStore %205 %254 +%255 = OpCompositeExtract %7 %249 3 +OpStore %206 %255 +%256 = OpCompositeExtract %8 %249 4 +%257 = OpFConvert %9 %256 +OpStore %207 %257 +%258 = OpCompositeExtract %9 %249 5 +OpStore %208 %258 +%259 = OpCompositeExtract %10 %249 6 +%260 = OpFConvert %11 %259 +OpStore %209 %260 +%261 = OpCompositeExtract %11 %249 7 +OpStore %210 %261 +OpReturn +OpFunctionEnd +%285 = OpFunction %2 None %51 +%262 = OpLabel +%287 = OpVariable %61 Function %288 +%265 = OpLoad %5 %264 +%266 = OpFConvert %4 %265 +%268 = OpLoad %5 %267 +%270 = OpLoad %7 %269 +%271 = OpFConvert %6 %270 +%273 = OpLoad %7 %272 +%275 = OpLoad %9 %274 +%276 = OpFConvert %8 %275 +%278 = OpLoad %9 %277 +%280 = OpLoad %11 %279 +%281 = OpFConvert %10 %280 +%283 = OpLoad %11 %282 +%263 = OpCompositeConstruct %12 %266 %268 %271 %273 %276 %278 %281 %283 +OpBranch %289 +%289 = OpLabel +OpLine %3 68 5 +OpStore %287 %263 +OpLine %3 69 5 +OpLine %3 69 5 +%290 = OpAccessChain %64 %287 %66 +OpStore %290 %286 +OpLine %3 70 12 +%291 = OpAccessChain %64 %287 %66 +%292 = OpLoad %4 %291 +%293 = OpFConvert %5 %292 +OpStore %284 %293 +OpReturn +OpFunctionEnd +%324 = OpFunction %2 None %51 +%294 = OpLabel +%325 = OpVariable %61 Function %326 +%297 = OpLoad %5 %296 +%298 = OpFConvert %4 %297 +%300 = OpLoad %5 %299 +%302 = OpLoad %7 %301 +%303 = OpFConvert %6 %302 +%305 = OpLoad %7 %304 +%307 = OpLoad %9 %306 +%308 = OpFConvert %8 %307 +%310 = OpLoad %9 %309 +%312 = OpLoad %11 %311 +%313 = OpFConvert %10 %312 +%315 = OpLoad %11 %314 +%295 = OpCompositeConstruct %12 %298 %300 %303 %305 %308 %310 %313 %315 +OpBranch %327 +%327 = OpLabel +OpLine %3 76 5 +%328 = OpCompositeExtract %6 %295 2 +%329 = OpCompositeExtract %4 %328 1 +OpLine %3 76 5 +%330 = OpAccessChain %64 %325 %75 %66 +OpStore %330 %329 +OpLine %3 77 5 +%331 = OpCompositeExtract %6 %295 2 +%332 = OpCompositeExtract %4 %331 0 +OpLine %3 77 5 +%333 = OpAccessChain %64 %325 %75 %71 +OpStore %333 %332 +OpLine %3 1 1 +%334 = OpLoad %12 %325 +%335 = OpCompositeExtract %4 %334 0 +%336 = OpFConvert %5 %335 +OpStore %316 %336 +%337 = OpCompositeExtract %5 %334 1 +OpStore %317 %337 +%338 = OpCompositeExtract %6 %334 2 +%339 = OpFConvert %7 %338 +OpStore %318 %339 +%340 = OpCompositeExtract %7 %334 3 +OpStore %319 %340 +%341 = OpCompositeExtract %8 %334 4 +%342 = OpFConvert %9 %341 +OpStore %320 %342 +%343 = OpCompositeExtract %9 %334 5 +OpStore %321 %343 +%344 = OpCompositeExtract %10 %334 6 +%345 = OpFConvert %11 %344 +OpStore %322 %345 +%346 = OpCompositeExtract %11 %334 7 +OpStore %323 %346 +OpReturn +OpFunctionEnd \ No newline at end of file diff --git a/naga/tests/out/spv/wgsl-f16.spvasm b/naga/tests/out/spv/wgsl-f16.spvasm index d6aed42b4eb..4fc6963ce87 100644 --- a/naga/tests/out/spv/wgsl-f16.spvasm +++ b/naga/tests/out/spv/wgsl-f16.spvasm @@ -100,8 +100,8 @@ OpMemberDecorate %41 0 Offset 0 %20 = OpTypeArray %3 %21 %22 = OpTypeStruct %20 %23 = OpTypeStruct %3 %3 %8 %3 %3 %4 -%24 = OpConstant %3 2.1524e-41 -%25 = OpConstant %3 2.7121e-41 +%24 = OpConstant %3 0.000000000000000000000000000000000000000021524 +%25 = OpConstant %3 0.000000000000000000000000000000000000000027121 %27 = OpTypePointer Private %3 %26 = OpVariable %27 Private %24 %29 = OpTypeStruct %19 @@ -124,13 +124,13 @@ OpMemberDecorate %41 0 Offset 0 %48 = OpConstant %4 0 %50 = OpTypePointer StorageBuffer %19 %52 = OpTypePointer StorageBuffer %22 -%56 = OpConstant %3 8.8991e-41 -%57 = OpConstant %3 2.4753e-41 +%56 = OpConstant %3 0.000000000000000000000000000000000000000088991 +%57 = OpConstant %3 0.000000000000000000000000000000000000000024753 %58 = OpConstant %5 65504 %59 = OpConstant %5 -65504 %60 = OpConstant %4 65504 -%61 = OpConstant %6 65504.0 -%62 = OpConstant %6 -65504.0 +%61 = OpConstant %6 65504 +%62 = OpConstant %6 -65504 %64 = OpTypePointer Function %23 %65 = OpConstantNull %23 %67 = OpTypePointer Function %3 @@ -192,7 +192,7 @@ OpMemberDecorate %41 0 Offset 0 %474 = OpTypeMatrix %344 4 %496 = OpTypeMatrix %350 4 %519 = OpTypeFunction %2 -%525 = OpConstant %3 2.2959e-41 +%525 = OpConstant %3 0.000000000000000000000000000000000000000022959 %528 = OpConstant %4 7 %45 = OpFunction %3 None %46 %44 = OpFunctionParameter %3 diff --git a/naga/tests/out/spv/wgsl-f64.spvasm b/naga/tests/out/spv/wgsl-f64.spvasm index d51bc3c8535..fd0fa038e1c 100644 --- a/naga/tests/out/spv/wgsl-f64.spvasm +++ b/naga/tests/out/spv/wgsl-f64.spvasm @@ -10,19 +10,19 @@ OpEntryPoint GLCompute %28 "main" OpExecutionMode %28 LocalSize 1 1 1 %2 = OpTypeVoid %3 = OpTypeFloat 64 -%4 = OpConstant %3 1.0 -%5 = OpConstant %3 2.0 +%4 = OpConstant %3 1 +%5 = OpConstant %3 2 %7 = OpTypePointer Private %3 %6 = OpVariable %7 Private %4 %11 = OpTypeFunction %3 %3 -%12 = OpConstant %3 30.0 -%13 = OpConstant %3 400.0 -%14 = OpConstant %3 5.0 -%15 = OpConstant %3 -1.0 +%12 = OpConstant %3 30 +%13 = OpConstant %3 400 +%14 = OpConstant %3 5 +%15 = OpConstant %3 -1 %17 = OpTypePointer Function %3 %18 = OpConstantNull %3 %29 = OpTypeFunction %2 -%30 = OpConstant %3 6.0 +%30 = OpConstant %3 6 %10 = OpFunction %3 None %11 %9 = OpFunctionParameter %3 %8 = OpLabel diff --git a/naga/tests/out/spv/wgsl-fragment-output.spvasm b/naga/tests/out/spv/wgsl-fragment-output.spvasm index 414c646c73c..cad9ebf7b24 100644 --- a/naga/tests/out/spv/wgsl-fragment-output.spvasm +++ b/naga/tests/out/spv/wgsl-fragment-output.spvasm @@ -61,7 +61,7 @@ OpDecorate %80 Location 5 %29 = OpTypePointer Output %11 %28 = OpVariable %29 Output %31 = OpTypeFunction %2 -%32 = OpConstant %3 0.0 +%32 = OpConstant %3 0 %33 = OpConstantComposite %4 %32 %32 %32 %32 %34 = OpConstant %5 0 %35 = OpConstantComposite %6 %34 %34 %34 %34 diff --git a/naga/tests/out/spv/wgsl-functions-optimized-by-capability.spvasm b/naga/tests/out/spv/wgsl-functions-optimized-by-capability.spvasm index 00fc02f9f8b..011a5e39e34 100644 --- a/naga/tests/out/spv/wgsl-functions-optimized-by-capability.spvasm +++ b/naga/tests/out/spv/wgsl-functions-optimized-by-capability.spvasm @@ -3,8 +3,8 @@ ; Generator: rspirv ; Bound: 30 OpCapability Shader -OpCapability DotProductKHR -OpCapability DotProductInput4x8BitPackedKHR +OpCapability DotProduct +OpCapability DotProductInput4x8BitPacked OpExtension "SPV_KHR_integer_dot_product" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -27,14 +27,14 @@ OpExecutionMode %26 LocalSize 1 1 1 %4 = OpLabel OpBranch %15 %15 = OpLabel -%17 = OpSDotKHR %16 %7 %8 PackedVectorFormat4x8BitKHR -%18 = OpUDotKHR %3 %9 %10 PackedVectorFormat4x8BitKHR +%17 = OpSDot %16 %7 %8 PackedVectorFormat4x8Bit +%18 = OpUDot %3 %9 %10 PackedVectorFormat4x8Bit %19 = OpIAdd %3 %11 %18 %20 = OpIAdd %3 %12 %18 -%21 = OpSDotKHR %16 %19 %20 PackedVectorFormat4x8BitKHR +%21 = OpSDot %16 %19 %20 PackedVectorFormat4x8Bit %22 = OpIAdd %3 %13 %18 %23 = OpIAdd %3 %14 %18 -%24 = OpUDotKHR %3 %22 %23 PackedVectorFormat4x8BitKHR +%24 = OpUDot %3 %22 %23 PackedVectorFormat4x8Bit OpReturnValue %24 OpFunctionEnd %26 = OpFunction %2 None %27 diff --git a/naga/tests/out/spv/wgsl-functions-optimized-by-version.spvasm b/naga/tests/out/spv/wgsl-functions-optimized-by-version.spvasm index 0c77fbda039..46aa7520ca1 100644 --- a/naga/tests/out/spv/wgsl-functions-optimized-by-version.spvasm +++ b/naga/tests/out/spv/wgsl-functions-optimized-by-version.spvasm @@ -3,8 +3,8 @@ ; Generator: rspirv ; Bound: 30 OpCapability Shader -OpCapability DotProductKHR -OpCapability DotProductInput4x8BitPackedKHR +OpCapability DotProduct +OpCapability DotProductInput4x8BitPacked %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 OpEntryPoint GLCompute %26 "main" @@ -26,14 +26,14 @@ OpExecutionMode %26 LocalSize 1 1 1 %4 = OpLabel OpBranch %15 %15 = OpLabel -%17 = OpSDotKHR %16 %7 %8 PackedVectorFormat4x8BitKHR -%18 = OpUDotKHR %3 %9 %10 PackedVectorFormat4x8BitKHR +%17 = OpSDot %16 %7 %8 PackedVectorFormat4x8Bit +%18 = OpUDot %3 %9 %10 PackedVectorFormat4x8Bit %19 = OpIAdd %3 %11 %18 %20 = OpIAdd %3 %12 %18 -%21 = OpSDotKHR %16 %19 %20 PackedVectorFormat4x8BitKHR +%21 = OpSDot %16 %19 %20 PackedVectorFormat4x8Bit %22 = OpIAdd %3 %13 %18 %23 = OpIAdd %3 %14 %18 -%24 = OpUDotKHR %3 %22 %23 PackedVectorFormat4x8BitKHR +%24 = OpUDot %3 %22 %23 PackedVectorFormat4x8Bit OpReturnValue %24 OpFunctionEnd %26 = OpFunction %2 None %27 diff --git a/naga/tests/out/spv/wgsl-functions.spvasm b/naga/tests/out/spv/wgsl-functions.spvasm index 35585e2a6c6..a84e322ae74 100644 --- a/naga/tests/out/spv/wgsl-functions.spvasm +++ b/naga/tests/out/spv/wgsl-functions.spvasm @@ -3,8 +3,8 @@ ; Generator: rspirv ; Bound: 95 OpCapability Shader -OpCapability DotProductKHR -OpCapability DotProductInput4x8BitPackedKHR +OpCapability DotProduct +OpCapability DotProductInput4x8BitPacked OpExtension "SPV_KHR_integer_dot_product" %1 = OpExtInstImport "GLSL.std.450" OpMemoryModel Logical GLSL450 @@ -16,7 +16,7 @@ OpExecutionMode %89 LocalSize 1 1 1 %5 = OpTypeInt 32 1 %6 = OpTypeInt 32 0 %9 = OpTypeFunction %3 -%10 = OpConstant %4 2.0 +%10 = OpConstant %4 2 %11 = OpConstantComposite %3 %10 %10 %12 = OpConstant %4 0.5 %13 = OpConstantComposite %3 %12 %12 @@ -96,14 +96,14 @@ OpFunctionEnd %69 = OpLabel OpBranch %79 %79 = OpLabel -%80 = OpSDotKHR %5 %22 %72 PackedVectorFormat4x8BitKHR -%81 = OpUDotKHR %6 %73 %74 PackedVectorFormat4x8BitKHR +%80 = OpSDot %5 %22 %72 PackedVectorFormat4x8Bit +%81 = OpUDot %6 %73 %74 PackedVectorFormat4x8Bit %82 = OpIAdd %6 %75 %81 %83 = OpIAdd %6 %76 %81 -%84 = OpSDotKHR %5 %82 %83 PackedVectorFormat4x8BitKHR +%84 = OpSDot %5 %82 %83 PackedVectorFormat4x8Bit %85 = OpIAdd %6 %77 %81 %86 = OpIAdd %6 %78 %81 -%87 = OpUDotKHR %6 %85 %86 PackedVectorFormat4x8BitKHR +%87 = OpUDot %6 %85 %86 PackedVectorFormat4x8Bit OpReturnValue %87 OpFunctionEnd %89 = OpFunction %2 None %90 diff --git a/naga/tests/out/spv/wgsl-globals.spvasm b/naga/tests/out/spv/wgsl-globals.spvasm index 5cf31f80766..8feae9610ce 100644 --- a/naga/tests/out/spv/wgsl-globals.spvasm +++ b/naga/tests/out/spv/wgsl-globals.spvasm @@ -106,11 +106,11 @@ OpDecorate %116 BuiltIn LocalInvocationId %58 = OpTypeFunction %2 %59 = OpTypePointer StorageBuffer %9 %60 = OpConstant %7 0 -%62 = OpConstant %4 1.0 +%62 = OpConstant %4 1 %63 = OpConstantComposite %8 %62 %62 %62 %64 = OpConstant %23 1 -%65 = OpConstant %4 2.0 -%66 = OpConstant %4 3.0 +%65 = OpConstant %4 2 +%66 = OpConstant %4 3 %67 = OpConstantNull %24 %69 = OpTypePointer Function %23 %71 = OpTypePointer StorageBuffer %8 @@ -121,7 +121,7 @@ OpDecorate %116 BuiltIn LocalInvocationId %101 = OpTypePointer Uniform %15 %103 = OpTypePointer Uniform %19 %105 = OpTypePointer Uniform %22 -%107 = OpConstant %4 4.0 +%107 = OpConstant %4 4 %109 = OpTypePointer Function %4 %111 = OpTypePointer Function %3 %113 = OpConstantNull %5 diff --git a/naga/tests/out/spv/wgsl-image.spvasm b/naga/tests/out/spv/wgsl-image.spvasm index 25681afe888..541fb1f5e64 100644 --- a/naga/tests/out/spv/wgsl-image.spvasm +++ b/naga/tests/out/spv/wgsl-image.spvasm @@ -405,8 +405,8 @@ OpDecorate %549 Location 0 %117 = OpTypeVector %5 4 %132 = OpTypeVector %15 3 %195 = OpVariable %94 Input -%212 = OpConstant %8 0.0 -%213 = OpConstant %8 4294967000.0 +%212 = OpConstant %8 0 +%213 = OpConstant %8 4294967000 %219 = OpTypePointer Output %24 %218 = OpVariable %219 Output %229 = OpConstant %5 0 @@ -420,12 +420,12 @@ OpDecorate %549 Location 0 %314 = OpConstant %15 3 %315 = OpConstantComposite %14 %314 %88 %316 = OpConstant %8 2.3 -%317 = OpConstant %8 2.0 +%317 = OpConstant %8 2 %319 = OpTypePointer Function %24 %320 = OpConstantNull %24 %322 = OpTypeSampledImage %16 %327 = OpTypeSampledImage %17 -%351 = OpConstant %8 1.0 +%351 = OpConstant %8 1 %352 = OpConstantComposite %310 %351 %351 %359 = OpTypeSampledImage %19 %420 = OpTypeSampledImage %21 diff --git a/naga/tests/out/spv/wgsl-index-by-value.spvasm b/naga/tests/out/spv/wgsl-index-by-value.spvasm index 09fedc4d676..e9ed297e2bf 100644 --- a/naga/tests/out/spv/wgsl-index-by-value.spvasm +++ b/naga/tests/out/spv/wgsl-index-by-value.spvasm @@ -36,10 +36,10 @@ OpDecorate %77 BuiltIn Position %36 = OpConstantComposite %9 %32 %35 %38 = OpTypePointer Function %9 %46 = OpTypeFunction %10 %3 %3 -%47 = OpConstant %10 1.0 -%48 = OpConstant %10 2.0 -%49 = OpConstant %10 3.0 -%50 = OpConstant %10 4.0 +%47 = OpConstant %10 1 +%48 = OpConstant %10 2 +%49 = OpConstant %10 3 +%50 = OpConstant %10 4 %51 = OpConstantComposite %12 %47 %48 %52 = OpConstantComposite %12 %49 %50 %53 = OpConstantComposite %11 %51 %52 diff --git a/naga/tests/out/spv/wgsl-int64.spvasm b/naga/tests/out/spv/wgsl-int64.spvasm index 141a95be586..c35098f7bd2 100644 --- a/naga/tests/out/spv/wgsl-int64.spvasm +++ b/naga/tests/out/spv/wgsl-int64.spvasm @@ -99,8 +99,8 @@ OpMemberDecorate %36 0 Offset 0 %78 = OpTypePointer Uniform %6 %79 = OpConstant %5 1 %88 = OpTypePointer Uniform %7 -%94 = OpConstant %7 -9.223372e18 -%95 = OpConstant %7 9.2233715e18 +%94 = OpConstant %7 -9223372000000000000 +%95 = OpConstant %7 9223371500000000000 %100 = OpTypePointer Uniform %3 %101 = OpConstant %5 7 %108 = OpTypePointer Uniform %4 @@ -128,8 +128,8 @@ OpMemberDecorate %36 0 Offset 0 %230 = OpConstant %4 18446744073709551615 %231 = OpConstant %4 5 %233 = OpTypePointer Function %4 -%264 = OpConstant %7 0.0 -%265 = OpConstant %7 1.8446743e19 +%264 = OpConstant %7 0 +%265 = OpConstant %7 18446743000000000000 %299 = OpTypePointer StorageBuffer %4 %306 = OpTypePointer StorageBuffer %8 %313 = OpTypePointer StorageBuffer %9 diff --git a/naga/tests/out/spv/wgsl-interface.fragment.spvasm b/naga/tests/out/spv/wgsl-interface.fragment.spvasm index 891b10d863d..e78d69121f3 100644 --- a/naga/tests/out/spv/wgsl-interface.fragment.spvasm +++ b/naga/tests/out/spv/wgsl-interface.fragment.spvasm @@ -56,8 +56,8 @@ OpDecorate %34 Location 0 %32 = OpVariable %33 Output %34 = OpVariable %31 Output %36 = OpTypeFunction %2 -%37 = OpConstant %3 0.0 -%38 = OpConstant %3 1.0 +%37 = OpConstant %3 0 +%38 = OpConstant %3 1 %35 = OpFunction %2 None %36 %14 = OpLabel %18 = OpLoad %4 %16 diff --git a/naga/tests/out/spv/wgsl-interface.vertex.spvasm b/naga/tests/out/spv/wgsl-interface.vertex.spvasm index 63ca57fa7d6..fa11c5b89f7 100644 --- a/naga/tests/out/spv/wgsl-interface.vertex.spvasm +++ b/naga/tests/out/spv/wgsl-interface.vertex.spvasm @@ -42,7 +42,7 @@ OpDecorate %26 BuiltIn PointSize %25 = OpTypePointer Output %3 %24 = OpVariable %25 Output %26 = OpVariable %25 Output -%27 = OpConstant %3 1.0 +%27 = OpConstant %3 1 %29 = OpTypeFunction %2 %30 = OpConstantComposite %4 %27 %27 %27 %27 %28 = OpFunction %2 None %29 diff --git a/naga/tests/out/spv/wgsl-interface.vertex_two_structs.spvasm b/naga/tests/out/spv/wgsl-interface.vertex_two_structs.spvasm index cad89cc551a..f83a5a624b3 100644 --- a/naga/tests/out/spv/wgsl-interface.vertex_two_structs.spvasm +++ b/naga/tests/out/spv/wgsl-interface.vertex_two_structs.spvasm @@ -38,10 +38,10 @@ OpDecorate %24 BuiltIn PointSize %22 = OpVariable %23 Output %25 = OpTypePointer Output %3 %24 = OpVariable %25 Output -%26 = OpConstant %3 1.0 +%26 = OpConstant %3 1 %28 = OpTypeFunction %2 %29 = OpConstant %6 2 -%30 = OpConstant %3 0.0 +%30 = OpConstant %3 0 %32 = OpTypePointer Function %6 %27 = OpFunction %2 None %28 %14 = OpLabel diff --git a/naga/tests/out/spv/wgsl-interpolate.spvasm b/naga/tests/out/spv/wgsl-interpolate.spvasm index ce0a9211522..91af2f3fe2c 100644 --- a/naga/tests/out/spv/wgsl-interpolate.spvasm +++ b/naga/tests/out/spv/wgsl-interpolate.spvasm @@ -176,36 +176,36 @@ OpDecorate %135 Location 11 %26 = OpVariable %18 Output %27 = OpVariable %18 Output %28 = OpVariable %18 Output -%29 = OpConstant %4 1.0 +%29 = OpConstant %4 1 %31 = OpTypeFunction %2 -%32 = OpConstant %4 2.0 -%33 = OpConstant %4 4.0 -%34 = OpConstant %4 5.0 -%35 = OpConstant %4 6.0 +%32 = OpConstant %4 2 +%33 = OpConstant %4 4 +%34 = OpConstant %4 5 +%35 = OpConstant %4 6 %36 = OpConstantComposite %5 %32 %33 %34 %35 %37 = OpConstant %6 8 %38 = OpConstant %6 9 %39 = OpConstant %6 10 -%40 = OpConstant %4 27.0 -%41 = OpConstant %4 64.0 -%42 = OpConstant %4 125.0 +%40 = OpConstant %4 27 +%41 = OpConstant %4 64 +%42 = OpConstant %4 125 %43 = OpConstantComposite %7 %41 %42 -%44 = OpConstant %4 216.0 -%45 = OpConstant %4 343.0 -%46 = OpConstant %4 512.0 +%44 = OpConstant %4 216 +%45 = OpConstant %4 343 +%46 = OpConstant %4 512 %47 = OpConstantComposite %8 %44 %45 %46 -%48 = OpConstant %4 255.0 -%49 = OpConstant %4 511.0 -%50 = OpConstant %4 1024.0 +%48 = OpConstant %4 255 +%49 = OpConstant %4 511 +%50 = OpConstant %4 1024 %51 = OpConstantComposite %8 %48 %49 %50 -%52 = OpConstant %4 729.0 -%53 = OpConstant %4 1000.0 -%54 = OpConstant %4 1331.0 -%55 = OpConstant %4 1728.0 +%52 = OpConstant %4 729 +%53 = OpConstant %4 1000 +%54 = OpConstant %4 1331 +%55 = OpConstant %4 1728 %56 = OpConstantComposite %5 %52 %53 %54 %55 -%57 = OpConstant %4 2197.0 -%58 = OpConstant %4 2744.0 -%59 = OpConstant %4 2812.0 +%57 = OpConstant %4 2197 +%58 = OpConstant %4 2744 +%59 = OpConstant %4 2812 %61 = OpTypePointer Function %9 %62 = OpConstantNull %9 %64 = OpTypePointer Function %5 diff --git a/naga/tests/out/spv/wgsl-interpolate_compat.spvasm b/naga/tests/out/spv/wgsl-interpolate_compat.spvasm index f6c72a053ef..f09a39d5a52 100644 --- a/naga/tests/out/spv/wgsl-interpolate_compat.spvasm +++ b/naga/tests/out/spv/wgsl-interpolate_compat.spvasm @@ -169,35 +169,35 @@ OpDecorate %129 Location 11 %25 = OpVariable %17 Output %26 = OpVariable %17 Output %27 = OpVariable %17 Output -%28 = OpConstant %4 1.0 +%28 = OpConstant %4 1 %30 = OpTypeFunction %2 -%31 = OpConstant %4 2.0 -%32 = OpConstant %4 4.0 -%33 = OpConstant %4 5.0 -%34 = OpConstant %4 6.0 +%31 = OpConstant %4 2 +%32 = OpConstant %4 4 +%33 = OpConstant %4 5 +%34 = OpConstant %4 6 %35 = OpConstantComposite %5 %31 %32 %33 %34 %36 = OpConstant %6 8 %37 = OpConstant %6 10 -%38 = OpConstant %4 27.0 -%39 = OpConstant %4 64.0 -%40 = OpConstant %4 125.0 +%38 = OpConstant %4 27 +%39 = OpConstant %4 64 +%40 = OpConstant %4 125 %41 = OpConstantComposite %7 %39 %40 -%42 = OpConstant %4 216.0 -%43 = OpConstant %4 343.0 -%44 = OpConstant %4 512.0 +%42 = OpConstant %4 216 +%43 = OpConstant %4 343 +%44 = OpConstant %4 512 %45 = OpConstantComposite %8 %42 %43 %44 -%46 = OpConstant %4 255.0 -%47 = OpConstant %4 511.0 -%48 = OpConstant %4 1024.0 +%46 = OpConstant %4 255 +%47 = OpConstant %4 511 +%48 = OpConstant %4 1024 %49 = OpConstantComposite %8 %46 %47 %48 -%50 = OpConstant %4 729.0 -%51 = OpConstant %4 1000.0 -%52 = OpConstant %4 1331.0 -%53 = OpConstant %4 1728.0 +%50 = OpConstant %4 729 +%51 = OpConstant %4 1000 +%52 = OpConstant %4 1331 +%53 = OpConstant %4 1728 %54 = OpConstantComposite %5 %50 %51 %52 %53 -%55 = OpConstant %4 2197.0 -%56 = OpConstant %4 2744.0 -%57 = OpConstant %4 2812.0 +%55 = OpConstant %4 2197 +%56 = OpConstant %4 2744 +%57 = OpConstant %4 2812 %59 = OpTypePointer Function %9 %60 = OpConstantNull %9 %62 = OpTypePointer Function %5 diff --git a/naga/tests/out/spv/wgsl-math-functions.spvasm b/naga/tests/out/spv/wgsl-math-functions.spvasm index a85644d4548..9aba53bc248 100644 --- a/naga/tests/out/spv/wgsl-math-functions.spvasm +++ b/naga/tests/out/spv/wgsl-math-functions.spvasm @@ -33,12 +33,12 @@ OpMemberDecorate %15 1 Offset 16 %15 = OpTypeStruct %4 %6 %16 = OpTypeVector %3 3 %19 = OpTypeFunction %2 -%20 = OpConstant %3 1.0 -%21 = OpConstant %3 0.0 +%20 = OpConstant %3 1 +%21 = OpConstant %3 0 %22 = OpConstantComposite %4 %21 %21 %21 %21 %23 = OpConstant %5 -1 %24 = OpConstantComposite %6 %23 %23 %23 %23 -%25 = OpConstant %3 -1.0 +%25 = OpConstant %3 -1 %26 = OpConstantComposite %4 %25 %25 %25 %25 %27 = OpConstantNull %7 %28 = OpConstant %9 4294967295 @@ -54,7 +54,7 @@ OpMemberDecorate %15 1 Offset 16 %38 = OpConstant %9 31 %39 = OpConstantComposite %8 %38 %38 %40 = OpConstant %5 2 -%41 = OpConstant %3 2.0 +%41 = OpConstant %3 2 %42 = OpConstantComposite %10 %20 %41 %43 = OpConstant %5 3 %44 = OpConstant %5 4 diff --git a/naga/tests/out/spv/wgsl-operators.spvasm b/naga/tests/out/spv/wgsl-operators.spvasm index cf24cc8102f..9ffe1ae1ea2 100644 --- a/naga/tests/out/spv/wgsl-operators.spvasm +++ b/naga/tests/out/spv/wgsl-operators.spvasm @@ -21,9 +21,9 @@ OpDecorate %513 BuiltIn WorkgroupId %12 = OpTypeMatrix %8 4 %13 = OpTypeMatrix %4 3 %14 = OpTypeVector %5 3 -%15 = OpConstant %3 1.0 +%15 = OpConstant %3 1 %16 = OpConstantComposite %4 %15 %15 %15 %15 -%17 = OpConstant %3 0.0 +%17 = OpConstant %3 0 %18 = OpConstantComposite %4 %17 %17 %17 %17 %19 = OpConstant %3 0.5 %20 = OpConstantComposite %4 %19 %19 %19 %19 @@ -44,17 +44,17 @@ OpDecorate %513 BuiltIn WorkgroupId %60 = OpConstantComposite %6 %58 %58 %58 %58 %65 = OpConstantComposite %6 %21 %21 %21 %21 %72 = OpTypeFunction %4 %3 %5 -%73 = OpConstant %3 2.0 +%73 = OpConstant %3 2 %74 = OpConstantComposite %7 %73 %73 -%75 = OpConstant %3 4.0 +%75 = OpConstant %3 4 %76 = OpConstantComposite %7 %75 %75 -%77 = OpConstant %3 8.0 +%77 = OpConstant %3 8 %78 = OpConstantComposite %7 %77 %77 %79 = OpConstant %5 2 %80 = OpConstantComposite %6 %79 %79 %79 %79 %93 = OpTypeFunction %7 %94 = OpConstantComposite %7 %15 %15 -%95 = OpConstant %3 3.0 +%95 = OpConstant %3 3 %96 = OpConstantComposite %7 %95 %95 %98 = OpTypePointer Function %7 %110 = OpTypeFunction %8 %8 diff --git a/naga/tests/out/spv/wgsl-overrides-ray-query.main.spvasm b/naga/tests/out/spv/wgsl-overrides-ray-query.main.spvasm index 4ceae75e596..34a8df87711 100644 --- a/naga/tests/out/spv/wgsl-overrides-ray-query.main.spvasm +++ b/naga/tests/out/spv/wgsl-overrides-ray-query.main.spvasm @@ -19,24 +19,24 @@ OpDecorate %10 DescriptorSet 0 OpDecorate %10 Binding 0 %2 = OpTypeVoid %3 = OpTypeFloat 32 -%4 = OpTypeAccelerationStructureNV +%4 = OpTypeAccelerationStructureKHR %5 = OpTypeRayQueryKHR %6 = OpTypeInt 32 0 %7 = OpTypeVector %3 3 %8 = OpTypeStruct %6 %6 %3 %3 %7 %7 -%9 = OpConstant %3 2.0 +%9 = OpConstant %3 2 %11 = OpTypePointer UniformConstant %4 %10 = OpVariable %11 UniformConstant %14 = OpTypeFunction %2 %16 = OpConstant %6 4 %17 = OpConstant %6 255 -%18 = OpConstant %3 34.0 -%19 = OpConstant %3 38.0 -%20 = OpConstant %3 46.0 +%18 = OpConstant %3 34 +%19 = OpConstant %3 38 +%20 = OpConstant %3 46 %21 = OpConstantComposite %7 %20 %20 %20 -%22 = OpConstant %3 58.0 -%23 = OpConstant %3 62.0 -%24 = OpConstant %3 74.0 +%22 = OpConstant %3 58 +%23 = OpConstant %3 62 +%24 = OpConstant %3 74 %25 = OpConstantComposite %7 %22 %23 %24 %26 = OpConstantComposite %8 %16 %17 %18 %19 %21 %25 %28 = OpTypePointer Function %5 diff --git a/naga/tests/out/spv/wgsl-overrides.main.spvasm b/naga/tests/out/spv/wgsl-overrides.main.spvasm index a685272479e..3b9eaca2497 100644 --- a/naga/tests/out/spv/wgsl-overrides.main.spvasm +++ b/naga/tests/out/spv/wgsl-overrides.main.spvasm @@ -13,21 +13,21 @@ OpExecutionMode %22 LocalSize 1 1 1 %5 = OpTypeInt 32 0 %6 = OpConstantTrue %3 %7 = OpConstant %4 2.3 -%8 = OpConstant %4 0.0 +%8 = OpConstant %4 0 %9 = OpConstantFalse %3 %10 = OpConstant %4 1.1 -%11 = OpConstant %4 2.0 +%11 = OpConstant %4 2 %12 = OpConstant %4 4.6 %13 = OpConstant %4 2.718 %14 = OpConstant %5 0 -%15 = OpConstant %4 10.0 -%16 = OpConstant %4 11.0 +%15 = OpConstant %4 10 +%16 = OpConstant %4 11 %18 = OpTypePointer Private %4 %17 = OpVariable %18 Private %16 %20 = OpConstantNull %4 %19 = OpVariable %18 Private %20 %23 = OpTypeFunction %2 -%24 = OpConstant %4 23.0 +%24 = OpConstant %4 23 %26 = OpTypePointer Function %4 %28 = OpTypePointer Function %3 %29 = OpConstantNull %3 diff --git a/naga/tests/out/spv/wgsl-padding.spvasm b/naga/tests/out/spv/wgsl-padding.spvasm index 2df29c96d4b..615965cc3e4 100644 --- a/naga/tests/out/spv/wgsl-padding.spvasm +++ b/naga/tests/out/spv/wgsl-padding.spvasm @@ -107,7 +107,7 @@ OpDecorate %25 BuiltIn Position %30 = OpConstant %10 0 %32 = OpTypePointer Uniform %11 %34 = OpTypePointer Uniform %13 -%36 = OpConstant %4 1.0 +%36 = OpConstant %4 1 %37 = OpConstantComposite %14 %36 %36 %36 %36 %39 = OpTypePointer Uniform %4 %40 = OpConstant %10 1 diff --git a/naga/tests/out/spv/wgsl-pointers.spvasm b/naga/tests/out/spv/wgsl-pointers.spvasm index 779b01b8af9..08867c95384 100644 --- a/naga/tests/out/spv/wgsl-pointers.spvasm +++ b/naga/tests/out/spv/wgsl-pointers.spvasm @@ -71,7 +71,7 @@ OpDecorate %11 Binding 0 %12 = OpTypePointer StorageBuffer %9 %11 = OpVariable %12 StorageBuffer %15 = OpTypeFunction %2 -%16 = OpConstant %6 10.0 +%16 = OpConstant %6 10 %17 = OpConstantComposite %5 %16 %16 %19 = OpTypePointer Function %4 %20 = OpConstantNull %4 diff --git a/naga/tests/out/spv/wgsl-policy-mix.spvasm b/naga/tests/out/spv/wgsl-policy-mix.spvasm index 28e8d92226f..8d9209d7b35 100644 --- a/naga/tests/out/spv/wgsl-policy-mix.spvasm +++ b/naga/tests/out/spv/wgsl-policy-mix.spvasm @@ -120,8 +120,8 @@ OpDecorate %115 BuiltIn LocalInvocationId %42 = OpConstant %8 0 %44 = OpTypePointer Uniform %12 %47 = OpConstant %4 0.707 -%48 = OpConstant %4 0.0 -%49 = OpConstant %4 1.0 +%48 = OpConstant %4 0 +%49 = OpConstant %4 1 %50 = OpConstantComposite %5 %47 %48 %48 %49 %51 = OpConstantComposite %5 %48 %47 %48 %49 %52 = OpConstantComposite %20 %50 %51 diff --git a/naga/tests/out/spv/wgsl-quad.spvasm b/naga/tests/out/spv/wgsl-quad.spvasm index a3532fb16e0..307fa304926 100644 --- a/naga/tests/out/spv/wgsl-quad.spvasm +++ b/naga/tests/out/spv/wgsl-quad.spvasm @@ -97,8 +97,8 @@ OpDecorate %60 Location 0 %24 = OpTypePointer Output %6 %23 = OpVariable %24 Output %26 = OpTypeFunction %2 -%27 = OpConstant %4 0.0 -%28 = OpConstant %4 1.0 +%27 = OpConstant %4 0 +%28 = OpConstant %4 1 %35 = OpTypePointer Output %4 %37 = OpTypeInt 32 0 %36 = OpConstant %37 1 diff --git a/naga/tests/out/spv/wgsl-ray-query.spvasm b/naga/tests/out/spv/wgsl-ray-query.spvasm index 138efe2c352..d49ae40b2f8 100644 --- a/naga/tests/out/spv/wgsl-ray-query.spvasm +++ b/naga/tests/out/spv/wgsl-ray-query.spvasm @@ -43,7 +43,7 @@ OpMemberDecorate %18 0 Offset 0 %2 = OpTypeVoid %3 = OpTypeFloat 32 %4 = OpTypeVector %3 3 -%5 = OpTypeAccelerationStructureNV +%5 = OpTypeAccelerationStructureKHR %6 = OpTypeInt 32 0 %7 = OpTypeVector %3 2 %8 = OpTypeBool @@ -62,7 +62,7 @@ OpMemberDecorate %18 0 Offset 0 %27 = OpConstant %6 4 %28 = OpConstant %6 255 %29 = OpConstant %3 0.1 -%30 = OpConstant %3 100.0 +%30 = OpConstant %3 100 %32 = OpTypePointer Function %11 %45 = OpTypeVector %6 2 %46 = OpTypePointer Function %45 @@ -89,9 +89,9 @@ OpMemberDecorate %18 0 Offset 0 %113 = OpConstant %6 7 %115 = OpConstant %6 8 %123 = OpTypeFunction %4 %4 %10 -%124 = OpConstant %3 1.0 +%124 = OpConstant %3 1 %125 = OpConstant %3 2.4 -%126 = OpConstant %3 0.0 +%126 = OpConstant %3 0 %141 = OpTypeFunction %2 %143 = OpTypePointer StorageBuffer %13 %145 = OpConstantComposite %4 %126 %126 %126 @@ -99,7 +99,7 @@ OpMemberDecorate %18 0 Offset 0 %149 = OpTypePointer StorageBuffer %6 %154 = OpTypePointer StorageBuffer %4 %162 = OpConstantComposite %12 %27 %28 %29 %30 %145 %146 -%163 = OpConstant %3 10.0 +%163 = OpConstant %3 10 %76 = OpFunction %10 None %75 %78 = OpFunctionParameter %32 %79 = OpLabel diff --git a/naga/tests/out/spv/wgsl-select.spvasm b/naga/tests/out/spv/wgsl-select.spvasm index d0011c464f8..9725ab38beb 100644 --- a/naga/tests/out/spv/wgsl-select.spvasm +++ b/naga/tests/out/spv/wgsl-select.spvasm @@ -13,11 +13,11 @@ OpExecutionMode %8 LocalSize 1 1 1 %6 = OpTypeInt 32 1 %5 = OpTypeVector %6 2 %9 = OpTypeFunction %2 -%10 = OpConstant %4 1.0 +%10 = OpConstant %4 1 %11 = OpConstant %6 1 %12 = OpConstant %6 2 %13 = OpConstantComposite %5 %11 %12 -%14 = OpConstant %4 0.0 +%14 = OpConstant %4 0 %15 = OpConstantComposite %3 %10 %14 %16 = OpConstantComposite %3 %14 %10 %18 = OpTypePointer Function %5 diff --git a/naga/tests/out/spv/wgsl-separate-entry-points.fragment.spvasm b/naga/tests/out/spv/wgsl-separate-entry-points.fragment.spvasm index e29ce8f15db..ae58bae874f 100644 --- a/naga/tests/out/spv/wgsl-separate-entry-points.fragment.spvasm +++ b/naga/tests/out/spv/wgsl-separate-entry-points.fragment.spvasm @@ -12,7 +12,7 @@ OpDecorate %14 Location 0 %4 = OpTypeFloat 32 %3 = OpTypeVector %4 4 %7 = OpTypeFunction %2 -%8 = OpConstant %4 0.0 +%8 = OpConstant %4 0 %15 = OpTypePointer Output %3 %14 = OpVariable %15 Output %17 = OpConstantNull %3 diff --git a/naga/tests/out/spv/wgsl-shadow.spvasm b/naga/tests/out/spv/wgsl-shadow.spvasm index ea131ae6d03..1360528131a 100644 --- a/naga/tests/out/spv/wgsl-shadow.spvasm +++ b/naga/tests/out/spv/wgsl-shadow.spvasm @@ -267,8 +267,8 @@ OpDecorate %227 Location 0 %40 = OpTypePointer UniformConstant %21 %39 = OpVariable %40 UniformConstant %45 = OpTypeFunction %4 %7 %6 -%48 = OpConstant %4 0.0 -%49 = OpConstant %4 1.0 +%48 = OpConstant %4 0 +%49 = OpConstant %4 1 %50 = OpConstant %4 0.5 %51 = OpConstant %4 -0.5 %52 = OpConstantComposite %22 %50 %51 diff --git a/naga/tests/out/spv/wgsl-skybox.spvasm b/naga/tests/out/spv/wgsl-skybox.spvasm index 7bc7b30f6da..f3e4be2cc22 100644 --- a/naga/tests/out/spv/wgsl-skybox.spvasm +++ b/naga/tests/out/spv/wgsl-skybox.spvasm @@ -65,9 +65,9 @@ OpDecorate %105 Location 0 %48 = OpTypePointer Uniform %8 %49 = OpConstant %9 0 %51 = OpConstant %10 2 -%52 = OpConstant %4 4.0 -%53 = OpConstant %4 1.0 -%54 = OpConstant %4 0.0 +%52 = OpConstant %4 4 +%53 = OpConstant %4 1 +%54 = OpConstant %4 0 %56 = OpTypePointer Function %10 %57 = OpConstantNull %10 %59 = OpConstantNull %10 diff --git a/naga/tests/out/spv/wgsl-storage-textures.spvasm b/naga/tests/out/spv/wgsl-storage-textures.spvasm index c043c2b94e2..167d5f49299 100644 --- a/naga/tests/out/spv/wgsl-storage-textures.spvasm +++ b/naga/tests/out/spv/wgsl-storage-textures.spvasm @@ -48,7 +48,7 @@ OpDecorate %15 Binding 2 %24 = OpTypeVector %22 2 %25 = OpConstantComposite %24 %23 %23 %27 = OpTypeVector %4 4 -%36 = OpConstant %4 0.0 +%36 = OpConstant %4 0 %37 = OpConstantComposite %27 %36 %36 %36 %36 %17 = OpFunction %2 None %18 %16 = OpLabel diff --git a/naga/tests/out/spv/wgsl-struct-layout.spvasm b/naga/tests/out/spv/wgsl-struct-layout.spvasm index 42899de9b58..31611dad6a9 100644 --- a/naga/tests/out/spv/wgsl-struct-layout.spvasm +++ b/naga/tests/out/spv/wgsl-struct-layout.spvasm @@ -76,7 +76,7 @@ OpDecorate %77 BuiltIn Position %29 = OpTypePointer Output %6 %28 = OpVariable %29 Output %31 = OpTypeFunction %2 -%32 = OpConstant %3 0.0 +%32 = OpConstant %3 0 %33 = OpConstantComposite %6 %32 %32 %32 %32 %37 = OpVariable %23 Input %39 = OpVariable %26 Input diff --git a/naga/tests/out/spv/wgsl-texture-arg.spvasm b/naga/tests/out/spv/wgsl-texture-arg.spvasm index ebb110e8fd8..23bd0ce2977 100644 --- a/naga/tests/out/spv/wgsl-texture-arg.spvasm +++ b/naga/tests/out/spv/wgsl-texture-arg.spvasm @@ -44,7 +44,7 @@ OpDecorate %27 Location 0 %12 = OpTypePointer UniformConstant %6 %11 = OpVariable %12 UniformConstant %19 = OpTypeFunction %7 %10 %12 -%20 = OpConstant %5 0.0 +%20 = OpConstant %5 0 %21 = OpConstantComposite %8 %20 %20 %23 = OpTypeSampledImage %4 %28 = OpTypePointer Output %7 diff --git a/naga/tests/out/spv/wgsl-type-inference.spvasm b/naga/tests/out/spv/wgsl-type-inference.spvasm index adcaa8094e6..d56e1fdac6b 100644 --- a/naga/tests/out/spv/wgsl-type-inference.spvasm +++ b/naga/tests/out/spv/wgsl-type-inference.spvasm @@ -15,11 +15,11 @@ OpExecutionMode %18 LocalSize 1 1 1 %8 = OpTypeVector %4 2 %7 = OpTypeMatrix %8 2 %9 = OpConstant %3 1 -%10 = OpConstant %4 1.0 +%10 = OpConstant %4 1 %11 = OpConstantNull %6 %12 = OpConstant %5 1 %13 = OpConstantComposite %6 %12 %12 %12 %12 -%14 = OpConstant %4 0.0 +%14 = OpConstant %4 0 %15 = OpConstantComposite %8 %14 %14 %16 = OpConstantComposite %7 %15 %15 %19 = OpTypeFunction %2 diff --git a/naga/tests/out/wgsl/glsl-bits_glsl.frag.wgsl b/naga/tests/out/wgsl/glsl-bits.frag.wgsl similarity index 100% rename from naga/tests/out/wgsl/glsl-bits_glsl.frag.wgsl rename to naga/tests/out/wgsl/glsl-bits.frag.wgsl diff --git a/naga/tests/out/wgsl/spv-8151-barrier-reorder.wgsl b/naga/tests/out/wgsl/spv-8151-barrier-reorder.wgsl new file mode 100644 index 00000000000..4ba429bb811 --- /dev/null +++ b/naga/tests/out/wgsl/spv-8151-barrier-reorder.wgsl @@ -0,0 +1,30 @@ +struct type_3 { + member: array, +} + +var global: vec3; +@group(0) @binding(0) +var global_1: type_3; +var global_2: u32; + +fn function() { + let _e6 = global; + let _e8 = (_e6.x == 0u); + if _e8 { + global_2 = 1u; + } + workgroupBarrier(); + let _e9 = global_2; + workgroupBarrier(); + global_1.member[_e6.x] = _e9; + if _e8 { + global_2 = 2u; + } + return; +} + +@compute @workgroup_size(2, 1, 1) +fn barrier_reorder_bug(@builtin(local_invocation_id) param: vec3) { + global = param; + function(); +} diff --git a/naga/tests/out/wgsl/spv-atomic_compare_exchange.wgsl b/naga/tests/out/wgsl/spv-atomic_compare_exchange.wgsl index 21456b064c9..36b460d74af 100644 --- a/naga/tests/out/wgsl/spv-atomic_compare_exchange.wgsl +++ b/naga/tests/out/wgsl/spv-atomic_compare_exchange.wgsl @@ -61,6 +61,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn stagetest_atomic_compare_exchange() { +fn stage_test_atomic_compare_exchange() { function(); } diff --git a/naga/tests/out/wgsl/spv-atomic_exchange.wgsl b/naga/tests/out/wgsl/spv-atomic_exchange.wgsl index f954a850795..0440b1b92b5 100644 --- a/naga/tests/out/wgsl/spv-atomic_exchange.wgsl +++ b/naga/tests/out/wgsl/spv-atomic_exchange.wgsl @@ -75,6 +75,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn stagetest_atomic_exchange() { +fn stage_test_atomic_exchange() { function(); } diff --git a/naga/tests/out/wgsl/spv-atomic_i_add_sub.wgsl b/naga/tests/out/wgsl/spv-atomic_i_add_sub.wgsl index d7798bd411d..bebab9e059e 100644 --- a/naga/tests/out/wgsl/spv-atomic_i_add_sub.wgsl +++ b/naga/tests/out/wgsl/spv-atomic_i_add_sub.wgsl @@ -21,6 +21,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn stagetest_atomic_i_add_sub() { +fn stage_test_atomic_i_add_sub() { function(); } diff --git a/naga/tests/out/wgsl/spv-atomic_i_decrement.wgsl b/naga/tests/out/wgsl/spv-atomic_i_decrement.wgsl index 190256e1c97..e2f3bd83b6e 100644 --- a/naga/tests/out/wgsl/spv-atomic_i_decrement.wgsl +++ b/naga/tests/out/wgsl/spv-atomic_i_decrement.wgsl @@ -32,6 +32,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn stagetest_atomic_i_decrement() { +fn stage_test_atomic_i_decrement() { function(); } diff --git a/naga/tests/out/wgsl/spv-atomic_i_increment.wgsl b/naga/tests/out/wgsl/spv-atomic_i_increment.wgsl index f7282aa3e64..139372776a3 100644 --- a/naga/tests/out/wgsl/spv-atomic_i_increment.wgsl +++ b/naga/tests/out/wgsl/spv-atomic_i_increment.wgsl @@ -37,6 +37,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn stagetest_atomic_i_increment() { +fn stage_test_atomic_i_increment() { function(); } diff --git a/naga/tests/out/wgsl/spv-atomic_load_and_store.wgsl b/naga/tests/out/wgsl/spv-atomic_load_and_store.wgsl index 9bfaa4ef55e..0525acb2232 100644 --- a/naga/tests/out/wgsl/spv-atomic_load_and_store.wgsl +++ b/naga/tests/out/wgsl/spv-atomic_load_and_store.wgsl @@ -67,6 +67,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn stagetest_atomic_load_and_store() { +fn stage_test_atomic_load_and_store() { function(); } diff --git a/naga/tests/out/wgsl/spv-binding-arrays.runtime.wgsl b/naga/tests/out/wgsl/spv-binding-arrays.runtime.wgsl new file mode 100644 index 00000000000..72b8244c573 --- /dev/null +++ b/naga/tests/out/wgsl/spv-binding-arrays.runtime.wgsl @@ -0,0 +1,24 @@ +var input_u002e_texture_coordinates_1: vec2; +var input_u002e_texture_index_1: u32; +@group(0) @binding(0) +var textures: binding_array>; +@group(0) @binding(1) +var linear_sampler: sampler; +var entryPointParam_main: vec4; + +fn main_1() { + let _e5 = input_u002e_texture_coordinates_1; + let _e6 = input_u002e_texture_index_1; + let _e8 = textureSample(textures[_e6], linear_sampler, _e5); + entryPointParam_main = _e8; + return; +} + +@fragment +fn main(@location(0) input_u002e_texture_coordinates: vec2, @location(1) @interpolate(flat) input_u002e_texture_index: u32) -> @location(0) vec4 { + input_u002e_texture_coordinates_1 = input_u002e_texture_coordinates; + input_u002e_texture_index_1 = input_u002e_texture_index; + main_1(); + let _e5 = entryPointParam_main; + return _e5; +} diff --git a/naga/tests/out/wgsl/spv-builtin-accessed-outside-entrypoint.wgsl b/naga/tests/out/wgsl/spv-builtin-accessed-outside-entrypoint.wgsl index 8e1b885bf72..2941b766365 100644 --- a/naga/tests/out/wgsl/spv-builtin-accessed-outside-entrypoint.wgsl +++ b/naga/tests/out/wgsl/spv-builtin-accessed-outside-entrypoint.wgsl @@ -8,7 +8,7 @@ struct gl_PerVertex { var unnamed: gl_PerVertex = gl_PerVertex(vec4(0f, 0f, 0f, 1f), 1f, array(), array()); var gl_VertexIndex_1: i32; -fn builtin_usage() { +fn builtin_usage_u0028_() { let _e9 = gl_VertexIndex_1; let _e12 = gl_VertexIndex_1; unnamed.gl_Position = vec4(select(1f, -4f, (_e9 == 0i)), select(-1f, 4f, (_e12 == 2i)), 0f, 1f); @@ -16,7 +16,7 @@ fn builtin_usage() { } fn main_1() { - builtin_usage(); + builtin_usage_u0028_(); return; } diff --git a/naga/tests/out/wgsl/spv-do-while.wgsl b/naga/tests/out/wgsl/spv-do-while.wgsl index 0b7e3afbcce..3a6d43d95dc 100644 --- a/naga/tests/out/wgsl/spv-do-while.wgsl +++ b/naga/tests/out/wgsl/spv-do-while.wgsl @@ -1,4 +1,4 @@ -fn fb1_(cond: ptr) { +fn f_u0028_b1_u003b(cond: ptr) { loop { continue; continuing { @@ -13,7 +13,7 @@ fn main_1() { var param: bool; param = false; - fb1_((¶m)); + f_u0028_b1_u003b((¶m)); return; } diff --git a/naga/tests/out/wgsl/spv-fetch_depth.wgsl b/naga/tests/out/wgsl/spv-fetch_depth.wgsl index 3e206bc2676..17950e9b1e2 100644 --- a/naga/tests/out/wgsl/spv-fetch_depth.wgsl +++ b/naga/tests/out/wgsl/spv-fetch_depth.wgsl @@ -21,6 +21,6 @@ fn function() { } @compute @workgroup_size(32, 1, 1) -fn cullfetch_depth() { +fn cull_fetch_depth() { function(); } diff --git a/naga/tests/out/wgsl/spv-gather-cmp.wgsl b/naga/tests/out/wgsl/spv-gather-cmp.wgsl new file mode 100644 index 00000000000..2685fa003f8 --- /dev/null +++ b/naga/tests/out/wgsl/spv-gather-cmp.wgsl @@ -0,0 +1,21 @@ +var input_u002e_texture_coordinates_1: vec2; +@group(0) @binding(0) +var texture: texture_depth_2d; +@group(0) @binding(1) +var depth_sampler: sampler_comparison; +var entryPointParam_main: vec4; + +fn main_1() { + let _e5 = input_u002e_texture_coordinates_1; + let _e6 = textureGatherCompare(texture, depth_sampler, _e5, 0.5f); + entryPointParam_main = _e6; + return; +} + +@fragment +fn main(@location(0) input_u002e_texture_coordinates: vec2) -> @location(0) vec4 { + input_u002e_texture_coordinates_1 = input_u002e_texture_coordinates; + main_1(); + let _e3 = entryPointParam_main; + return _e3; +} diff --git a/naga/tests/out/wgsl/spv-gather.wgsl b/naga/tests/out/wgsl/spv-gather.wgsl new file mode 100644 index 00000000000..a35708b89c7 --- /dev/null +++ b/naga/tests/out/wgsl/spv-gather.wgsl @@ -0,0 +1,21 @@ +var input_u002e_texture_coordinates_1: vec2; +@group(0) @binding(0) +var texture: texture_2d; +@group(0) @binding(1) +var linear_sampler: sampler; +var entryPointParam_main: vec4; + +fn main_1() { + let _e4 = input_u002e_texture_coordinates_1; + let _e5 = textureGather(1, texture, linear_sampler, _e4); + entryPointParam_main = _e5; + return; +} + +@fragment +fn main(@location(0) input_u002e_texture_coordinates: vec2) -> @location(0) vec4 { + input_u002e_texture_coordinates_1 = input_u002e_texture_coordinates; + main_1(); + let _e3 = entryPointParam_main; + return _e3; +} diff --git a/naga/tests/out/wgsl/spv-load-ms-texture.wgsl b/naga/tests/out/wgsl/spv-load-ms-texture.wgsl new file mode 100644 index 00000000000..564a7524266 --- /dev/null +++ b/naga/tests/out/wgsl/spv-load-ms-texture.wgsl @@ -0,0 +1,37 @@ +var global: vec4; +var entryPointParam_fs_main: vec4; +@group(0) @binding(0) +var texture: texture_multisampled_2d; + +fn fs_main() { + var index: i32; + var color: vec4; + + let _e9 = global; + index = 0i; + loop { + let _e12 = index; + if (_e12 < 8i) { + } else { + break; + } + let _e14 = index; + let _e15 = textureLoad(texture, vec2(_e9.xy), _e14); + let _e16 = color; + let _e18 = index; + index = (_e18 + 1i); + color = (_e16 + _e15); + continue; + } + let _e20 = color; + entryPointParam_fs_main = (_e20 * vec4(0.125f, 0.125f, 0.125f, 0.125f)); + return; +} + +@fragment +fn main(@builtin(position) param: vec4) -> @location(0) vec4 { + global = param; + fs_main(); + let _e3 = entryPointParam_fs_main; + return _e3; +} diff --git a/naga/tests/out/wgsl/wgsl-7995-unicode-idents.wgsl b/naga/tests/out/wgsl/wgsl-7995-unicode-idents.wgsl new file mode 100644 index 00000000000..9a93d697454 --- /dev/null +++ b/naga/tests/out/wgsl/wgsl-7995-unicode-idents.wgsl @@ -0,0 +1,14 @@ +@group(0) @binding(0) +var asdf: f32; + +fn compute() -> f32 { + let _e1 = asdf; + let u03b8_2_ = (_e1 + 9001f); + return u03b8_2_; +} + +@compute @workgroup_size(1, 1, 1) +fn main() { + let _e0 = compute(); + return; +} diff --git a/naga/tests/out/wgsl/wgsl-control-flow.wgsl b/naga/tests/out/wgsl/wgsl-control-flow.wgsl index 4baf30ce6ee..c8256b30263 100644 --- a/naga/tests/out/wgsl/wgsl-control-flow.wgsl +++ b/naga/tests/out/wgsl/wgsl-control-flow.wgsl @@ -42,17 +42,36 @@ fn control_flow() { } case 2: { pos = 1i; - return; } case 3: { pos = 2i; - return; } case 4: { - return; } default: { pos = 3i; + } + } + let _e15 = pos; + switch _e15 { + case 1: { + pos = 0i; + return; + } + case 2: { + pos = 1i; + return; + } + case 3, 4: { + pos = 2i; + return; + } + case 5, 6: { + pos = 3i; + return; + } + default: { + pos = 4i; return; } } diff --git a/naga/tests/out/wgsl/wgsl-empty-if.wgsl b/naga/tests/out/wgsl/wgsl-empty-if.wgsl new file mode 100644 index 00000000000..5435020695d --- /dev/null +++ b/naga/tests/out/wgsl/wgsl-empty-if.wgsl @@ -0,0 +1,6 @@ +@compute @workgroup_size(1, 1, 1) +fn comp(@builtin(global_invocation_id) id: vec3) { + if (id.x == 0u) { + } + return; +} diff --git a/naga/tests/out/wgsl/wgsl-texture-external.wgsl b/naga/tests/out/wgsl/wgsl-texture-external.wgsl new file mode 100644 index 00000000000..329895ec88e --- /dev/null +++ b/naga/tests/out/wgsl/wgsl-texture-external.wgsl @@ -0,0 +1,43 @@ +@group(0) @binding(0) +var tex: texture_external; +@group(0) @binding(1) +var samp: sampler; + +fn test(t: texture_external) -> vec4 { + var a: vec4; + var b: vec4; + var c: vec4; + var d: vec2; + + let _e4 = textureSampleBaseClampToEdge(t, samp, vec2(0f)); + a = _e4; + let _e8 = textureLoad(t, vec2(0i)); + b = _e8; + let _e12 = textureLoad(t, vec2(0u)); + c = _e12; + let _e14 = textureDimensions(t); + d = _e14; + let _e16 = a; + let _e17 = b; + let _e19 = c; + let _e21 = d; + return (((_e16 + _e17) + _e19) + vec2(_e21).xyxy); +} + +@fragment +fn fragment_main() -> @location(0) vec4 { + let _e1 = test(tex); + return _e1; +} + +@vertex +fn vertex_main() -> @builtin(position) vec4 { + let _e1 = test(tex); + return _e1; +} + +@compute @workgroup_size(1, 1, 1) +fn compute_main() { + let _e1 = test(tex); + return; +} diff --git a/naga/xtask/Cargo.toml b/naga/xtask/Cargo.toml index a56a07515df..8b921bc3124 100644 --- a/naga/xtask/Cargo.toml +++ b/naga/xtask/Cargo.toml @@ -3,7 +3,7 @@ name = "naga-xtask" version = "0.1.0" edition = "2021" publish = false -rust-version = "1.84" +rust-version.workspace = true [target.'cfg(not(target_arch = "wasm32"))'.dependencies] anyhow.workspace = true diff --git a/naga/xtask/src/validate.rs b/naga/xtask/src/validate.rs index d1d75783ea8..44728584810 100644 --- a/naga/xtask/src/validate.rs +++ b/naga/xtask/src/validate.rs @@ -30,7 +30,7 @@ pub(crate) fn validate(cmd: ValidateSubcommand) -> anyhow::Result<()> { Ok(result) => result, Err(payload) => Err(match payload.downcast_ref::<&str>() { Some(message) => { - anyhow::anyhow!("Validation job thread panicked: {}", message) + anyhow::anyhow!("Validation job thread panicked: {message}") } None => anyhow::anyhow!("Validation job thread panicked"), }), @@ -46,7 +46,7 @@ pub(crate) fn validate(cmd: ValidateSubcommand) -> anyhow::Result<()> { if let Err(error) = result { all_good = false; progress_bar.suspend(|| { - log::error!("{:#}", error); + log::error!("{error:#}"); }); } progress_bar.inc(1); @@ -60,7 +60,7 @@ pub(crate) fn validate(cmd: ValidateSubcommand) -> anyhow::Result<()> { ); if let Err(error) = enqueuing_thread.join().unwrap() { - bail!("Error enqueuing jobs:\n{:#}", error); + bail!("Error enqueuing jobs:\n{error:#}"); } Ok(()) diff --git a/player/Cargo.toml b/player/Cargo.toml index aadf4e5e8ff..f29aacf0e94 100644 --- a/player/Cargo.toml +++ b/player/Cargo.toml @@ -26,6 +26,7 @@ log.workspace = true raw-window-handle.workspace = true ron.workspace = true winit = { workspace = true, optional = true } +bytemuck.workspace = true # Non-Webassembly # diff --git a/player/src/bin/play.rs b/player/src/bin/play.rs index 719a5c09621..dfce80b8ad4 100644 --- a/player/src/bin/play.rs +++ b/player/src/bin/play.rs @@ -7,10 +7,12 @@ fn main() { use player::GlobalPlay as _; use wgc::device::trace; + use wgpu_core::identity::IdentityManager; use std::{ fs, path::{Path, PathBuf}, + process::exit, }; #[cfg(feature = "winit")] @@ -28,13 +30,28 @@ fn main() { //TODO: setting for the backend bits //TODO: setting for the target frame, or controls - let dir = match std::env::args().nth(1) { - Some(arg) if Path::new(&arg).is_dir() => PathBuf::from(arg), - _ => panic!("Provide the dir path as the parameter"), + const HELP: &str = "\ + Usage: play | \n\ + \n\ + Play a wgpu trace from the specified file or directory. If the trace contains\n\ + buffers, textures, or shaders, the directory form must be used.\n"; + + let (dir, trace) = match std::env::args().nth(1) { + Some(arg) if Path::new(&arg).is_dir() => ( + PathBuf::from(arg.clone()), + PathBuf::from(arg).join(trace::FILE_NAME), + ), + Some(arg) if Path::new(&arg).is_file() => { + (PathBuf::from("/nonexistent"), PathBuf::from(arg)) + } + _ => { + eprintln!("{HELP}"); + exit(1); + } }; - log::info!("Loading trace '{:?}'", dir); - let file = fs::File::open(dir.join(trace::FILE_NAME)).unwrap(); + log::info!("Loading trace '{trace:?}'"); + let file = fs::File::open(trace).unwrap(); let mut actions: Vec = ron::de::from_reader(file).unwrap(); actions.reverse(); // allows us to pop from the top log::info!("Found {} actions", actions.len()); @@ -51,8 +68,10 @@ fn main() { .build(&event_loop) .unwrap(); - let global = wgc::global::Global::new("player", &wgt::InstanceDescriptor::default()); - let mut command_buffer_id_manager = wgc::identity::IdentityManager::new(); + let global = + wgc::global::Global::new("player", &wgt::InstanceDescriptor::from_env_or_default()); + let mut command_encoder_id_manager = IdentityManager::new(); + let mut command_buffer_id_manager = IdentityManager::new(); #[cfg(feature = "winit")] let surface = unsafe { @@ -64,37 +83,39 @@ fn main() { } .unwrap(); - let (device, queue) = match actions.pop() { - Some(trace::Action::Init { desc, backend }) => { - log::info!("Initializing the device for backend: {:?}", backend); - let adapter = global - .request_adapter( - &wgc::instance::RequestAdapterOptions { - power_preference: wgt::PowerPreference::None, - force_fallback_adapter: false, - #[cfg(feature = "winit")] - compatible_surface: Some(surface), - #[cfg(not(feature = "winit"))] - compatible_surface: None, - }, - wgt::Backends::from(backend), - Some(wgc::id::AdapterId::zip(0, 1)), - ) - .expect("Unable to find an adapter for selected backend"); - - let info = global.adapter_get_info(adapter); - log::info!("Picked '{}'", info.name); - let device_id = wgc::id::Id::zip(0, 1); - let queue_id = wgc::id::Id::zip(0, 1); - let res = - global.adapter_request_device(adapter, &desc, Some(device_id), Some(queue_id)); - if let Err(e) = res { - panic!("{e:?}"); + let (backends, device_desc) = + match actions.pop_if(|action| matches!(action, trace::Action::Init { .. })) { + Some(trace::Action::Init { desc, backend }) => { + log::info!("Initializing the device for backend: {backend:?}"); + (wgt::Backends::from(backend), desc) } - (device_id, queue_id) - } - _ => panic!("Expected Action::Init"), - }; + Some(_) => unreachable!(), + None => (wgt::Backends::all(), wgt::DeviceDescriptor::default()), + }; + + let adapter = global + .request_adapter( + &wgc::instance::RequestAdapterOptions { + #[cfg(feature = "winit")] + compatible_surface: Some(surface), + #[cfg(not(feature = "winit"))] + compatible_surface: None, + ..Default::default() + }, + backends, + Some(wgc::id::AdapterId::zip(0, 1)), + ) + .expect("Unable to obtain an adapter"); + + let info = global.adapter_get_info(adapter); + log::info!("Using '{}'", info.name); + + let device = wgc::id::Id::zip(0, 1); + let queue = wgc::id::Id::zip(0, 1); + let res = global.adapter_request_device(adapter, &device_desc, Some(device), Some(queue)); + if let Err(e) = res { + panic!("{e:?}"); + } log::info!("Executing actions"); #[cfg(not(feature = "winit"))] @@ -102,11 +123,20 @@ fn main() { unsafe { global.device_start_graphics_debugger_capture(device) }; while let Some(action) = actions.pop() { - global.process(device, queue, action, &dir, &mut command_buffer_id_manager); + global.process( + device, + queue, + action, + &dir, + &mut command_encoder_id_manager, + &mut command_buffer_id_manager, + ); } unsafe { global.device_stop_graphics_debugger_capture(device) }; - global.device_poll(device, wgt::PollType::wait()).unwrap(); + global + .device_poll(device, wgt::PollType::wait_indefinitely()) + .unwrap(); } #[cfg(feature = "winit")] { @@ -143,18 +173,18 @@ fn main() { let error = global.surface_configure(surface, device, &config); if let Some(e) = error { - panic!("{:?}", e); + panic!("{e:?}"); } } } Some(trace::Action::Present(id)) => { frame_count += 1; - log::debug!("Presenting frame {}", frame_count); + log::debug!("Presenting frame {frame_count}"); global.surface_present(id).unwrap(); target.exit(); } Some(trace::Action::DiscardSurfaceTexture(id)) => { - log::debug!("Discarding frame {}", frame_count); + log::debug!("Discarding frame {frame_count}"); global.surface_texture_discard(id).unwrap(); target.exit(); } @@ -164,12 +194,13 @@ fn main() { queue, action, &dir, + &mut command_encoder_id_manager, &mut command_buffer_id_manager, ); } None => { if !done { - println!("Finished the end at frame {}", frame_count); + println!("Finished the end at frame {frame_count}"); done = true; } target.exit(); @@ -180,7 +211,7 @@ fn main() { if let Some(config) = resize_config.take() { let error = global.surface_configure(surface, device, &config); if let Some(e) = error { - panic!("{:?}", e); + panic!("{e:?}"); } } } @@ -198,7 +229,9 @@ fn main() { }, Event::LoopExiting => { log::info!("Closing"); - global.device_poll(device, wgt::PollType::wait()).unwrap(); + global + .device_poll(device, wgt::PollType::wait_indefinitely()) + .unwrap(); } _ => {} } diff --git a/player/src/lib.rs b/player/src/lib.rs index 16e31947149..437f51b14bb 100644 --- a/player/src/lib.rs +++ b/player/src/lib.rs @@ -6,7 +6,7 @@ extern crate wgpu_core as wgc; extern crate wgpu_types as wgt; -use wgc::device::trace; +use wgc::{command::Command, device::trace, identity::IdentityManager}; use std::{borrow::Cow, fs, path::Path}; @@ -14,7 +14,8 @@ pub trait GlobalPlay { fn encode_commands( &self, encoder: wgc::id::CommandEncoderId, - commands: Vec, + commands: Vec, + command_buffer_id_manager: &mut IdentityManager, ) -> wgc::id::CommandBufferId; fn process( &self, @@ -22,7 +23,8 @@ pub trait GlobalPlay { queue: wgc::id::QueueId, action: trace::Action, dir: &Path, - comb_manager: &mut wgc::identity::IdentityManager, + command_encoder_id_manager: &mut IdentityManager, + command_buffer_id_manager: &mut IdentityManager, ); } @@ -30,11 +32,12 @@ impl GlobalPlay for wgc::global::Global { fn encode_commands( &self, encoder: wgc::id::CommandEncoderId, - commands: Vec, + commands: Vec, + command_buffer_id_manager: &mut IdentityManager, ) -> wgc::id::CommandBufferId { for command in commands { match command { - trace::Command::CopyBufferToBuffer { + Command::CopyBufferToBuffer { src, src_offset, dst, @@ -45,31 +48,31 @@ impl GlobalPlay for wgc::global::Global { encoder, src, src_offset, dst, dst_offset, size, ) .unwrap(), - trace::Command::CopyBufferToTexture { src, dst, size } => self + Command::CopyBufferToTexture { src, dst, size } => self .command_encoder_copy_buffer_to_texture(encoder, &src, &dst, &size) .unwrap(), - trace::Command::CopyTextureToBuffer { src, dst, size } => self + Command::CopyTextureToBuffer { src, dst, size } => self .command_encoder_copy_texture_to_buffer(encoder, &src, &dst, &size) .unwrap(), - trace::Command::CopyTextureToTexture { src, dst, size } => self + Command::CopyTextureToTexture { src, dst, size } => self .command_encoder_copy_texture_to_texture(encoder, &src, &dst, &size) .unwrap(), - trace::Command::ClearBuffer { dst, offset, size } => self + Command::ClearBuffer { dst, offset, size } => self .command_encoder_clear_buffer(encoder, dst, offset, size) .unwrap(), - trace::Command::ClearTexture { + Command::ClearTexture { dst, subresource_range, } => self .command_encoder_clear_texture(encoder, dst, &subresource_range) .unwrap(), - trace::Command::WriteTimestamp { + Command::WriteTimestamp { query_set_id, query_index, } => self .command_encoder_write_timestamp(encoder, query_set_id, query_index) .unwrap(), - trace::Command::ResolveQuerySet { + Command::ResolveQuerySet { query_set_id, start_query, query_count, @@ -85,16 +88,14 @@ impl GlobalPlay for wgc::global::Global { destination_offset, ) .unwrap(), - trace::Command::PushDebugGroup(marker) => self + Command::PushDebugGroup(marker) => self .command_encoder_push_debug_group(encoder, &marker) .unwrap(), - trace::Command::PopDebugGroup => { - self.command_encoder_pop_debug_group(encoder).unwrap() - } - trace::Command::InsertDebugMarker(marker) => self + Command::PopDebugGroup => self.command_encoder_pop_debug_group(encoder).unwrap(), + Command::InsertDebugMarker(marker) => self .command_encoder_insert_debug_marker(encoder, &marker) .unwrap(), - trace::Command::RunComputePass { + Command::RunComputePass { base, timestamp_writes, } => { @@ -104,7 +105,7 @@ impl GlobalPlay for wgc::global::Global { timestamp_writes.as_ref(), ); } - trace::Command::RunRenderPass { + Command::RunRenderPass { base, target_colors, target_depth_stencil, @@ -120,7 +121,7 @@ impl GlobalPlay for wgc::global::Global { occlusion_query_set_id, ); } - trace::Command::BuildAccelerationStructures { blas, tlas } => { + Command::BuildAccelerationStructures { blas, tlas } => { let blas_iter = blas.iter().map(|x| { let geometries = match &x.geometries { wgc::ray_tracing::TraceBlasGeometries::TriangleGeometries( @@ -172,8 +173,11 @@ impl GlobalPlay for wgc::global::Global { } } } - let (cmd_buf, error) = - self.command_encoder_finish(encoder, &wgt::CommandBufferDescriptor { label: None }); + let (cmd_buf, error) = self.command_encoder_finish( + encoder, + &wgt::CommandBufferDescriptor { label: None }, + Some(command_buffer_id_manager.process()), + ); if let Some(e) = error { panic!("{e}"); } @@ -186,10 +190,11 @@ impl GlobalPlay for wgc::global::Global { queue: wgc::id::QueueId, action: trace::Action, dir: &Path, - comb_manager: &mut wgc::identity::IdentityManager, + command_encoder_id_manager: &mut IdentityManager, + command_buffer_id_manager: &mut IdentityManager, ) { use wgc::device::trace::Action; - log::debug!("action {:?}", action); + log::debug!("action {action:?}"); //TODO: find a way to force ID perishing without excessive `maintain()` calls. match action { Action::Init { .. } => { @@ -237,6 +242,19 @@ impl GlobalPlay for wgc::global::Global { Action::DestroyTextureView(id) => { self.texture_view_drop(id).unwrap(); } + Action::CreateExternalTexture { id, desc, planes } => { + let (_, error) = + self.device_create_external_texture(device, &desc, &planes, Some(id)); + if let Some(e) = error { + panic!("{e}"); + } + } + Action::FreeExternalTexture(id) => { + self.external_texture_destroy(id); + } + Action::DestroyExternalTexture(id) => { + self.external_texture_drop(id); + } Action::CreateSampler(id, desc) => { let (_, error) = self.device_create_sampler(device, &desc, Some(id)); if let Some(e) = error { @@ -280,7 +298,7 @@ impl GlobalPlay for wgc::global::Global { self.bind_group_drop(id); } Action::CreateShaderModule { id, desc, data } => { - log::debug!("Creating shader from {}", data); + log::debug!("Creating shader from {data}"); let code = fs::read_to_string(dir.join(&data)).unwrap(); let source = if data.ends_with(".wgsl") { wgc::pipeline::ShaderModuleSource::Wgsl(Cow::Owned(code.clone())) @@ -295,23 +313,89 @@ impl GlobalPlay for wgc::global::Global { println!("shader compilation error:\n---{code}\n---\n{e}"); } } + Action::CreateShaderModulePassthrough { + id, + data, + entry_point, + label, + num_workgroups, + runtime_checks, + } => { + let spirv = data.iter().find_map(|a| { + if a.ends_with(".spv") { + let data = fs::read(dir.join(a)).unwrap(); + assert!(data.len() % 4 == 0); + + Some(Cow::Owned(bytemuck::pod_collect_to_vec(&data))) + } else { + None + } + }); + let dxil = data.iter().find_map(|a| { + if a.ends_with(".dxil") { + let vec = std::fs::read(dir.join(a)).unwrap(); + Some(Cow::Owned(vec)) + } else { + None + } + }); + let hlsl = data.iter().find_map(|a| { + if a.ends_with(".hlsl") { + let code = fs::read_to_string(dir.join(a)).unwrap(); + Some(Cow::Owned(code)) + } else { + None + } + }); + let msl = data.iter().find_map(|a| { + if a.ends_with(".msl") { + let code = fs::read_to_string(dir.join(a)).unwrap(); + Some(Cow::Owned(code)) + } else { + None + } + }); + let glsl = data.iter().find_map(|a| { + if a.ends_with(".glsl") { + let code = fs::read_to_string(dir.join(a)).unwrap(); + Some(Cow::Owned(code)) + } else { + None + } + }); + let wgsl = data.iter().find_map(|a| { + if a.ends_with(".wgsl") { + let code = fs::read_to_string(dir.join(a)).unwrap(); + Some(Cow::Owned(code)) + } else { + None + } + }); + let desc = wgt::CreateShaderModuleDescriptorPassthrough { + entry_point, + label, + num_workgroups, + runtime_checks, + + spirv, + dxil, + hlsl, + msl, + glsl, + wgsl, + }; + let (_, error) = unsafe { + self.device_create_shader_module_passthrough(device, &desc, Some(id)) + }; + if let Some(e) = error { + println!("shader compilation error: {e}"); + } + } Action::DestroyShaderModule(id) => { self.shader_module_drop(id); } - Action::CreateComputePipeline { - id, - desc, - implicit_context, - } => { - let implicit_ids = - implicit_context - .as_ref() - .map(|ic| wgc::device::ImplicitPipelineIds { - root_id: ic.root_id, - group_ids: &ic.group_ids, - }); - let (_, error) = - self.device_create_compute_pipeline(device, &desc, Some(id), implicit_ids); + Action::CreateComputePipeline { id, desc } => { + let (_, error) = self.device_create_compute_pipeline(device, &desc, Some(id)); if let Some(e) = error { panic!("{e}"); } @@ -319,20 +403,14 @@ impl GlobalPlay for wgc::global::Global { Action::DestroyComputePipeline(id) => { self.compute_pipeline_drop(id); } - Action::CreateRenderPipeline { - id, - desc, - implicit_context, - } => { - let implicit_ids = - implicit_context - .as_ref() - .map(|ic| wgc::device::ImplicitPipelineIds { - root_id: ic.root_id, - group_ids: &ic.group_ids, - }); - let (_, error) = - self.device_create_render_pipeline(device, &desc, Some(id), implicit_ids); + Action::CreateRenderPipeline { id, desc } => { + let (_, error) = self.device_create_render_pipeline(device, &desc, Some(id)); + if let Some(e) = error { + panic!("{e}"); + } + } + Action::CreateMeshPipeline { id, desc } => { + let (_, error) = self.device_create_mesh_pipeline(device, &desc, Some(id)); if let Some(e) = error { panic!("{e}"); } @@ -403,12 +481,12 @@ impl GlobalPlay for wgc::global::Global { let (encoder, error) = self.device_create_command_encoder( device, &wgt::CommandEncoderDescriptor { label: None }, - Some(comb_manager.process().into_command_encoder_id()), + Some(command_encoder_id_manager.process()), ); if let Some(e) = error { panic!("{e}"); } - let cmdbuf = self.encode_commands(encoder, commands); + let cmdbuf = self.encode_commands(encoder, commands, command_buffer_id_manager); self.queue_submit(queue, &[cmdbuf]).unwrap(); } Action::CreateBlas { id, desc, sizes } => { diff --git a/player/tests/player/main.rs b/player/tests/player/main.rs index 18f76e76e2c..700a6739048 100644 --- a/player/tests/player/main.rs +++ b/player/tests/player/main.rs @@ -20,6 +20,7 @@ use std::{ path::{Path, PathBuf}, slice, }; +use wgc::identity::IdentityManager; #[derive(serde::Deserialize)] struct RawId { @@ -75,8 +76,7 @@ impl Test<'_> { _ => unreachable!(), }; let string = read_to_string(&path).unwrap().replace("Noop", backend_name); - ron::de::from_str(&string) - .unwrap_or_else(|e| panic!("{path:?}:{} {}", e.position.line, e.code)) + ron::de::from_str(&string).unwrap_or_else(|e| panic!("{path:?}:{} {}", e.span, e.code)) } fn run( @@ -94,6 +94,7 @@ impl Test<'_> { label: None, required_features: self.features, required_limits: wgt::Limits::default(), + experimental_features: unsafe { wgt::ExperimentalFeatures::enabled() }, memory_hints: wgt::MemoryHints::default(), trace: wgt::Trace::Off, }, @@ -104,7 +105,8 @@ impl Test<'_> { panic!("{e:?}"); } - let mut command_buffer_id_manager = wgc::identity::IdentityManager::new(); + let mut command_encoder_id_manager = IdentityManager::new(); + let mut command_buffer_id_manager = IdentityManager::new(); println!("\t\t\tRunning..."); for action in self.actions { global.process( @@ -112,6 +114,7 @@ impl Test<'_> { queue_id, action, dir, + &mut command_encoder_id_manager, &mut command_buffer_id_manager, ); } @@ -133,7 +136,13 @@ impl Test<'_> { println!("\t\t\tWaiting..."); global - .device_poll(device_id, wgt::PollType::wait()) + .device_poll( + device_id, + wgt::PollType::Wait { + submission_index: None, + timeout: Some(std::time::Duration::from_secs(1)), // Tests really shouldn't need longer than that! + }, + ) .unwrap(); for expect in self.expectations { @@ -242,6 +251,7 @@ impl Corpus { } } +#[cfg_attr(miri, ignore)] #[test] fn test_api() { env_logger::init(); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1ed8cd0e604..607c42c968f 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] -channel = "1.84" +channel = "1.88" components = ["rustfmt", "clippy"] targets = ["wasm32-unknown-unknown"] diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 17c131a026c..95301df9488 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -29,6 +29,9 @@ harness = true [features] webgl = ["wgpu/webgl"] +# This feature is not actually used by this package, but it being present somewhere in the workspace +# allows us to force the build to have profiling code enabled so we can test that configuration. +test-build-with-profiling = ["profiling/type-check"] [dependencies] wgpu = { workspace = true, features = ["noop"] } @@ -64,9 +67,11 @@ trybuild.workspace = true # Cargo-metadata doesn't compile on wasm due to old cargo-util-schemas dependency. cargo_metadata.workspace = true env_logger.workspace = true -nv-flip.workspace = true parking_lot = { workspace = true, features = ["deadlock_detection"] } +[target.'cfg(not(any(target_arch = "wasm32", miri)))'.dependencies] +nv-flip.workspace = true + # Webassembly [target.'cfg(target_arch = "wasm32")'.dependencies] console_log.workspace = true diff --git a/tests/src/config.rs b/tests/src/config.rs index 62d3e56091b..a73cb821a12 100644 --- a/tests/src/config.rs +++ b/tests/src/config.rs @@ -118,3 +118,5 @@ impl Default for GpuTestConfiguration { Self::new() } } + +pub type GpuTestInitializer = fn() -> GpuTestConfiguration; diff --git a/tests/src/expectations.rs b/tests/src/expectations.rs index a9375c37e05..78d4ece9744 100644 --- a/tests/src/expectations.rs +++ b/tests/src/expectations.rs @@ -36,7 +36,7 @@ use core::fmt; /// [skip]: super::TestParameters::skip /// [expect_fail]: super::TestParameters::expect_fail /// [`AdapterInfo`]: wgpu::AdapterInfo -#[derive(Default, Clone)] +#[derive(Default, Clone, PartialEq)] pub struct FailureCase { /// Backends expected to fail, or `None` for any backend. /// @@ -334,7 +334,7 @@ impl FailureReason { } } -#[derive(Default, Clone)] +#[derive(Default, Clone, PartialEq)] pub enum FailureBehavior { /// Assert that the test fails for the given reason. /// diff --git a/tests/src/image.rs b/tests/src/image.rs index dee861b22d0..93a0fb0b47a 100644 --- a/tests/src/image.rs +++ b/tests/src/image.rs @@ -7,7 +7,7 @@ use wgpu::*; use crate::TestingContext; -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(target_arch = "wasm32", miri)))] async fn read_png(path: impl AsRef, width: u32, height: u32) -> Option> { let data = match std::fs::read(&path) { Ok(f) => f, @@ -23,7 +23,10 @@ async fn read_png(path: impl AsRef, width: u32, height: u32) -> Option, width: u32, height: u32) -> Option, width: u32, @@ -64,7 +67,7 @@ async fn write_png( writer.write_image_data(data).unwrap(); } -#[cfg_attr(target_arch = "wasm32", allow(unused))] +#[cfg_attr(any(target_arch = "wasm32", miri), allow(unused))] fn add_alpha(input: &[u8]) -> Vec { input .chunks_exact(3) @@ -72,7 +75,7 @@ fn add_alpha(input: &[u8]) -> Vec { .collect() } -#[cfg_attr(target_arch = "wasm32", allow(unused))] +#[cfg_attr(any(target_arch = "wasm32", miri), allow(unused))] fn remove_alpha(input: &[u8]) -> Vec { input .chunks_exact(4) @@ -81,7 +84,7 @@ fn remove_alpha(input: &[u8]) -> Vec { .collect() } -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(target_arch = "wasm32", miri)))] fn print_flip(pool: &mut nv_flip::FlipPool) { println!("\tMean: {:.6}", pool.mean()); println!("\tMin Value: {:.6}", pool.min_value()); @@ -115,7 +118,7 @@ pub enum ComparisonType { } impl ComparisonType { - #[cfg(not(target_arch = "wasm32"))] + #[cfg(not(any(target_arch = "wasm32", miri)))] fn check(&self, pool: &mut nv_flip::FlipPool) -> bool { match *self { ComparisonType::Mean(v) => { @@ -148,7 +151,7 @@ impl ComparisonType { } } -#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(any(target_arch = "wasm32", miri)))] pub async fn compare_image_output( path: impl AsRef + AsRef, adapter_info: &wgpu::AdapterInfo, @@ -170,7 +173,7 @@ pub async fn compare_image_output( width, height, test_with_alpha, - png::Compression::Best, + png::Compression::High, ) .await; return; @@ -250,7 +253,7 @@ pub async fn compare_image_output( } } -#[cfg(target_arch = "wasm32")] +#[cfg(any(target_arch = "wasm32", miri))] pub async fn compare_image_output( path: impl AsRef + AsRef, adapter_info: &wgpu::AdapterInfo, @@ -259,13 +262,13 @@ pub async fn compare_image_output( test_with_alpha: &[u8], checks: &[ComparisonType], ) { - #[cfg(target_arch = "wasm32")] + #[cfg(any(target_arch = "wasm32", miri))] { let _ = (path, adapter_info, width, height, test_with_alpha, checks); } } -#[cfg_attr(target_arch = "wasm32", allow(unused))] +#[cfg_attr(any(target_arch = "wasm32", miri), allow(unused))] fn sanitize_for_path(s: &str) -> String { s.chars() .map(|ch| if ch.is_ascii_alphanumeric() { ch } else { '_' }) @@ -574,7 +577,7 @@ impl ReadbackBuffers { ) -> Vec { let buffer_slice = buffer.slice(..); buffer_slice.map_async(MapMode::Read, |_| ()); - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); let (block_width, block_height) = self.texture_format.block_dimensions(); let expected_bytes_per_row = (self.texture_width / block_width) * self.texture_format.block_copy_size(aspect).unwrap_or(4); diff --git a/tests/src/init.rs b/tests/src/init.rs index d2aac6f14e1..38073fa1b39 100644 --- a/tests/src/init.rs +++ b/tests/src/init.rs @@ -50,6 +50,7 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> backend_options: wgpu::BackendOptions { dx12: wgpu::Dx12BackendOptions { shader_compiler: dx12_shader_compiler, + ..Default::default() }, gl: wgpu::GlBackendOptions { fence_behavior: if cfg!(target_family = "wasm") { @@ -65,8 +66,15 @@ pub fn initialize_instance(backends: wgpu::Backends, params: &TestParameters) -> ..Default::default() } .with_env(), - // TODO(https://github.com/gfx-rs/wgpu/issues/7119): Enable noop backend? - noop: wgpu::NoopBackendOptions::default(), + // Allow the noop backend to be used in tests. This will not be used unless + // WGPU_GPU_TESTS_USE_NOOP_BACKEND env var is set, because wgpu-info will not + // enumerate the noop backend. + // + // However, we use wasm_bindgen_test to run tests on wasm, and wgpu + // will chose the noop on wasm32 for some reason. + noop: wgpu::NoopBackendOptions { + enable: !cfg!(target_arch = "wasm32"), + }, }, }) } @@ -155,6 +163,7 @@ pub async fn initialize_device( label: None, required_features: features, required_limits: limits, + experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }, memory_hints: wgpu::MemoryHints::MemoryUsage, trace: wgpu::Trace::Off, }) diff --git a/tests/src/isolation.rs b/tests/src/isolation.rs index d99f4250882..75d98c115d3 100644 --- a/tests/src/isolation.rs +++ b/tests/src/isolation.rs @@ -31,7 +31,7 @@ impl OneTestPerProcessGuard { // We never abort if we're on wasm. Wasm tests are inherently single threaded, and panics cannot // unwind the stack and trigger all the guards, so we don't actually need to check. if other_tests_in_flight && !cfg!(target_arch = "wasm32") { - log::error!("{}", OTHER_TEST_IN_PROGRESS_ERROR); + log::error!("{OTHER_TEST_IN_PROGRESS_ERROR}"); // Hard exit to call attention to the error std::process::abort(); } diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 5c33cf3eeef..22afd7ecf77 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -17,7 +17,7 @@ mod run; pub use init::initialize_html_canvas; pub use self::image::ComparisonType; -pub use config::GpuTestConfiguration; +pub use config::{GpuTestConfiguration, GpuTestInitializer}; #[doc(hidden)] pub use ctor; pub use expectations::{FailureApplicationReasons, FailureBehavior, FailureCase, FailureReason}; @@ -37,7 +37,7 @@ pub use wgpu_macros::gpu_test; pub fn fail( device: &wgpu::Device, callback: impl FnOnce() -> T, - expected_msg_substring: Option<&'static str>, + expected_msg_substring: Option<&str>, ) -> T { device.push_error_scope(wgpu::ErrorFilter::Validation); let result = callback(); @@ -114,17 +114,22 @@ pub fn did_oom(device: &wgpu::Device, callback: impl FnOnce() -> T) -> (bool, } /// Adds the necessary main function for our gpu test harness. +/// +/// Takes a single argument which is an expression that evaluates to `Vec`. #[macro_export] macro_rules! gpu_test_main { - () => { + ($tests: expr) => { #[cfg(target_arch = "wasm32")] wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser); #[cfg(target_arch = "wasm32")] - fn main() {} + fn main() { + // Ensure that value is used so that warnings don't happen. + let _ = $tests; + } #[cfg(not(target_arch = "wasm32"))] fn main() -> $crate::native::MainResult { - $crate::native::main() + $crate::native::main($tests) } }; } diff --git a/tests/src/native.rs b/tests/src/native.rs index eb12d22a5f9..be660bb7543 100644 --- a/tests/src/native.rs +++ b/tests/src/native.rs @@ -9,6 +9,7 @@ use parking_lot::Mutex; use crate::{ config::GpuTestConfiguration, params::TestInfo, report::AdapterReport, run::execute_test, + GpuTestInitializer, }; type NativeTestFuture = Pin + Send>>; @@ -76,28 +77,41 @@ pub static TEST_LIST: Mutex> = Mutex::new(Vec:: pub type MainResult = anyhow::Result<()>; /// Main function that runs every gpu function once for every adapter on the system. -pub fn main() -> MainResult { +pub fn main(tests: Vec) -> MainResult { use anyhow::Context; use crate::report::GpuReport; - let config_text = { - profiling::scope!("Reading .gpuconfig"); - &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) - .context("Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?")? - }; - let mut report = - GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?; + // If this environment variable is set, we will only enumerate the noop backend. The + // main use case is running tests with miri, where we can't even enumerate adapters, + // as we cannot load DLLs or make any external calls. + let use_noop = std::env::var("WGPU_GPU_TESTS_USE_NOOP_BACKEND").as_deref() == Ok("1"); + + let report = if use_noop { + GpuReport::noop_only() + } else { + let config_text = { + profiling::scope!("Reading .gpuconfig"); + &std::fs::read_to_string(format!("{}/../.gpuconfig", env!("CARGO_MANIFEST_DIR"))) + .context( + "Failed to read .gpuconfig, did you run the tests via `cargo xtask test`?", + )? + }; + let mut report = + GpuReport::from_json(config_text).context("Could not parse .gpuconfig JSON")?; + + // Filter out the adapters that are not part of WGPU_BACKEND. + let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default(); + report + .devices + .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend))); - // Filter out the adapters that are not part of WGPU_BACKEND. - let wgpu_backends = wgpu::Backends::from_env().unwrap_or_default(); - report - .devices - .retain(|report| wgpu_backends.contains(wgpu::Backends::from(report.info.backend))); + report + }; - let mut test_guard = TEST_LIST.lock(); // Iterate through all the tests. Creating a test per adapter. - execute_native(test_guard.drain(..).flat_map(|test| { + execute_native(tests.into_iter().flat_map(|initializer| { + let test = initializer(); report .devices .iter() diff --git a/tests/src/params.rs b/tests/src/params.rs index f2c4d531a15..d7339225cb0 100644 --- a/tests/src/params.rs +++ b/tests/src/params.rs @@ -41,7 +41,9 @@ impl Default for TestParameters { required_limits: Limits::downlevel_webgl2_defaults(), required_instance_flags: InstanceFlags::empty(), force_fxc: false, - skips: Vec::new(), + // By default we skip the noop backend, and enable it if the test + // parameters ask us to remove it. + skips: vec![FailureCase::backend(wgpu::Backends::NOOP)], failures: Vec::new(), } } @@ -94,6 +96,16 @@ impl TestParameters { self.skips.push(when); self } + + /// Enable testing against the noop backend and miri. + /// + /// The noop backend does not execute any operations, but allows us to test + /// validation and memory safety. + pub fn enable_noop(mut self) -> Self { + self.skips + .retain(|case| *case != FailureCase::backend(wgpu::Backends::NOOP)); + self + } } /// Information about a test, including if if it should be skipped. diff --git a/tests/src/report.rs b/tests/src/report.rs index b26bdbfaf39..ed3cc03e461 100644 --- a/tests/src/report.rs +++ b/tests/src/report.rs @@ -15,6 +15,20 @@ pub(crate) struct GpuReport { } impl GpuReport { + #[cfg(not(target_arch = "wasm32"))] + /// Creates a new GpuReport with a single noop adapter. + pub(crate) fn noop_only() -> Self { + GpuReport { + devices: vec![AdapterReport { + info: wgpu::hal::noop::adapter_info(), + features: Features::all(), + limits: wgpu::hal::noop::CAPABILITIES.limits, + downlevel_caps: wgpu::hal::noop::CAPABILITIES.downlevel, + texture_format_features: HashMap::new(), // todo + }], + } + } + #[cfg_attr(target_arch = "wasm32", allow(unused))] pub(crate) fn from_json(file: &str) -> serde_json::Result { profiling::scope!("Parsing .gpuconfig"); diff --git a/tests/src/run.rs b/tests/src/run.rs index ceeb8aca0ba..e066371d2e0 100644 --- a/tests/src/run.rs +++ b/tests/src/run.rs @@ -12,6 +12,7 @@ use crate::{ GpuTestConfiguration, }; +#[derive(Hash)] /// Parameters and resources handed to the test function. pub struct TestingContext { pub instance: Instance, diff --git a/tests/tests/wgpu-compile/main.rs b/tests/tests/wgpu-compile/main.rs index f35a0cd24b6..a5edfa73b45 100644 --- a/tests/tests/wgpu-compile/main.rs +++ b/tests/tests/wgpu-compile/main.rs @@ -1,5 +1,7 @@ +#![cfg(not(miri))] // Tests that ensure that various constructs that should not compile do not compile. +#[cfg_attr(miri, ignore)] #[test] fn compile_fail() { let t = trybuild::TestCases::new(); diff --git a/tests/tests/wgpu-dependency/main.rs b/tests/tests/wgpu-dependency/main.rs index baf3f1bde51..ac96afdb0e1 100644 --- a/tests/tests/wgpu-dependency/main.rs +++ b/tests/tests/wgpu-dependency/main.rs @@ -1,6 +1,6 @@ // Cargo-metadata doesn't compile on wasm due to old cargo-util-schemas dependency. // Since this test isn't dependent on the current architecture, we can just skip it on wasm without any issues. -#![cfg(not(target_arch = "wasm32"))] +#![cfg(not(any(target_arch = "wasm32", miri)))] use std::process::Command; diff --git a/tests/tests/wgpu-gpu/bgra8unorm_storage.rs b/tests/tests/wgpu-gpu/bgra8unorm_storage.rs index eaa549ab6fc..82f2b912cf4 100644 --- a/tests/tests/wgpu-gpu/bgra8unorm_storage.rs +++ b/tests/tests/wgpu-gpu/bgra8unorm_storage.rs @@ -2,7 +2,11 @@ use std::borrow::Cow; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(BGRA8_UNORM_STORAGE); +} const SHADER_SRC: &str = " @group(0) @binding(0) var tex: texture_storage_2d; @@ -142,7 +146,9 @@ static BGRA8_UNORM_STORAGE: GpuTestConfiguration = GpuTestConfiguration::new() let buffer_slice = readback_buffer.slice(..); buffer_slice.map_async(wgpu::MapMode::Read, Result::unwrap); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); { let texels = buffer_slice.get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/bind_group_layout_dedup.rs b/tests/tests/wgpu-gpu/bind_group_layout_dedup.rs index b97b9cff10e..ee91b907b0d 100644 --- a/tests/tests/wgpu-gpu/bind_group_layout_dedup.rs +++ b/tests/tests/wgpu-gpu/bind_group_layout_dedup.rs @@ -1,6 +1,19 @@ use std::num::NonZeroU64; -use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + BIND_GROUP_LAYOUT_DEDUPLICATION, + BIND_GROUP_LAYOUT_DEDUPLICATION_WITH_DROPPED_USER_HANDLE, + GET_DERIVED_BGL, + SEPARATE_PIPELINES_HAVE_INCOMPATIBLE_DERIVED_BGLS, + DERIVED_BGLS_INCOMPATIBLE_WITH_REGULAR_BGLS, + BIND_GROUP_LAYOUT_DEDUPLICATION_DERIVED, + ]); +} const SHADER_SRC: &str = " @group(0) @binding(0) @@ -27,7 +40,11 @@ const ENTRY: wgpu::BindGroupLayoutEntry = wgpu::BindGroupLayoutEntry { #[gpu_test] static BIND_GROUP_LAYOUT_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_async(bgl_dedupe); async fn bgl_dedupe(ctx: TestingContext) { @@ -107,7 +124,11 @@ async fn bgl_dedupe(ctx: TestingContext) { #[gpu_test] static BIND_GROUP_LAYOUT_DEDUPLICATION_WITH_DROPPED_USER_HANDLE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(bgl_dedupe_with_dropped_user_handle); // https://github.com/gfx-rs/wgpu/issues/4824 @@ -190,7 +211,11 @@ fn bgl_dedupe_with_dropped_user_handle(ctx: TestingContext) { #[gpu_test] static GET_DERIVED_BGL: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(get_derived_bgl); fn get_derived_bgl(ctx: TestingContext) { @@ -264,7 +289,11 @@ fn get_derived_bgl(ctx: TestingContext) { #[gpu_test] static SEPARATE_PIPELINES_HAVE_INCOMPATIBLE_DERIVED_BGLS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(separate_pipelines_have_incompatible_derived_bgls); fn separate_pipelines_have_incompatible_derived_bgls(ctx: TestingContext) { @@ -328,7 +357,11 @@ fn separate_pipelines_have_incompatible_derived_bgls(ctx: TestingContext) { #[gpu_test] static DERIVED_BGLS_INCOMPATIBLE_WITH_REGULAR_BGLS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(derived_bgls_incompatible_with_regular_bgls); fn derived_bgls_incompatible_with_regular_bgls(ctx: TestingContext) { @@ -399,7 +432,11 @@ fn derived_bgls_incompatible_with_regular_bgls(ctx: TestingContext) { #[gpu_test] static BIND_GROUP_LAYOUT_DEDUPLICATION_DERIVED: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(bgl_dedupe_derived); fn bgl_dedupe_derived(ctx: TestingContext) { diff --git a/tests/tests/wgpu-gpu/bind_groups.rs b/tests/tests/wgpu-gpu/bind_groups.rs index 6dc57c43246..a6924d34ead 100644 --- a/tests/tests/wgpu-gpu/bind_groups.rs +++ b/tests/tests/wgpu-gpu/bind_groups.rs @@ -1,7 +1,19 @@ use std::num::NonZeroU64; use wgpu::{BufferUsages, PollType}; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + MULTIPLE_BINDINGS_WITH_DIFFERENT_SIZES, + BIND_GROUP_NONFILTERING_LAYOUT_NONFILTERING_SAMPLER, + BIND_GROUP_NONFILTERING_LAYOUT_MIN_SAMPLER, + BIND_GROUP_NONFILTERING_LAYOUT_MAG_SAMPLER, + BIND_GROUP_NONFILTERING_LAYOUT_MIPMAP_SAMPLER, + ]); +} /// Create two bind groups against the same bind group layout, in the same /// compute pass, but against two different shaders that have different binding @@ -113,7 +125,7 @@ fn multiple_bindings_with_differing_sizes(ctx: TestingContext) { ctx.queue.write_buffer(&buffer, 0, &data); ctx.queue.submit(Some(encoder.finish())); - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); } /// Test `descriptor` against a bind group layout that requires non-filtering sampler. @@ -164,14 +176,15 @@ static MULTIPLE_BINDINGS_WITH_DIFFERENT_SIZES: GpuTestConfiguration = GpuTestCon .parameters( TestParameters::default() .limits(wgpu::Limits::downlevel_defaults()) - .expect_fail(FailureCase::always()), // https://github.com/gfx-rs/wgpu/issues/7359 + .expect_fail(FailureCase::always()) + .enable_noop(), // https://github.com/gfx-rs/wgpu/issues/7359 ) .run_sync(multiple_bindings_with_differing_sizes); #[gpu_test] static BIND_GROUP_NONFILTERING_LAYOUT_NONFILTERING_SAMPLER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { try_sampler_nonfiltering_layout( ctx, @@ -189,7 +202,7 @@ static BIND_GROUP_NONFILTERING_LAYOUT_NONFILTERING_SAMPLER: GpuTestConfiguration #[gpu_test] static BIND_GROUP_NONFILTERING_LAYOUT_MIN_SAMPLER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { try_sampler_nonfiltering_layout( ctx, @@ -207,7 +220,7 @@ static BIND_GROUP_NONFILTERING_LAYOUT_MIN_SAMPLER: GpuTestConfiguration = #[gpu_test] static BIND_GROUP_NONFILTERING_LAYOUT_MAG_SAMPLER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { try_sampler_nonfiltering_layout( ctx, @@ -225,7 +238,7 @@ static BIND_GROUP_NONFILTERING_LAYOUT_MAG_SAMPLER: GpuTestConfiguration = #[gpu_test] static BIND_GROUP_NONFILTERING_LAYOUT_MIPMAP_SAMPLER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { try_sampler_nonfiltering_layout( ctx, diff --git a/tests/tests/wgpu-gpu/binding_array/buffers.rs b/tests/tests/wgpu-gpu/binding_array/buffers.rs index 44a53aa39dd..0e3b3166a49 100644 --- a/tests/tests/wgpu-gpu/binding_array/buffers.rs +++ b/tests/tests/wgpu-gpu/binding_array/buffers.rs @@ -1,7 +1,18 @@ use std::num::{NonZeroU32, NonZeroU64}; use wgpu::*; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + BINDING_ARRAY_UNIFORM_BUFFERS, + PARTIAL_BINDING_ARRAY_UNIFORM_BUFFERS, + BINDING_ARRAY_STORAGE_BUFFERS, + PARTIAL_BINDING_ARRAY_STORAGE_BUFFERS, + ]); +} #[gpu_test] static BINDING_ARRAY_UNIFORM_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new() @@ -254,7 +265,7 @@ async fn binding_array_buffers( let slice = readback_buffer.slice(..); slice.map_async(MapMode::Read, |_| {}); - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); let data = slice.get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/binding_array/mod.rs b/tests/tests/wgpu-gpu/binding_array/mod.rs index 4b8972fcdb2..753b1101351 100644 --- a/tests/tests/wgpu-gpu/binding_array/mod.rs +++ b/tests/tests/wgpu-gpu/binding_array/mod.rs @@ -2,3 +2,10 @@ mod buffers; mod sampled_textures; mod samplers; mod storage_textures; + +pub fn all_tests(tests: &mut Vec) { + buffers::all_tests(tests); + sampled_textures::all_tests(tests); + samplers::all_tests(tests); + storage_textures::all_tests(tests); +} diff --git a/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs b/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs index 320cf3e1079..c445857a92f 100644 --- a/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs +++ b/tests/tests/wgpu-gpu/binding_array/sampled_textures.rs @@ -2,9 +2,17 @@ use std::num::NonZeroU32; use wgpu::*; use wgpu_test::{ - gpu_test, image::ReadbackBuffers, GpuTestConfiguration, TestParameters, TestingContext, + gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters, + TestingContext, }; +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + BINDING_ARRAY_SAMPLED_TEXTURES, + PARTIAL_BINDING_ARRAY_SAMPLED_TEXTURES, + ]); +} + #[gpu_test] static BINDING_ARRAY_SAMPLED_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( diff --git a/tests/tests/wgpu-gpu/binding_array/samplers.rs b/tests/tests/wgpu-gpu/binding_array/samplers.rs index b0c3cf36fad..d8dfefe21fe 100644 --- a/tests/tests/wgpu-gpu/binding_array/samplers.rs +++ b/tests/tests/wgpu-gpu/binding_array/samplers.rs @@ -1,7 +1,13 @@ use std::num::{NonZeroU32, NonZeroU64}; use wgpu::*; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(tests: &mut Vec) { + tests.extend([BINDING_ARRAY_SAMPLERS, PARTIAL_BINDING_ARRAY_SAMPLERS]); +} #[gpu_test] static BINDING_ARRAY_SAMPLERS: GpuTestConfiguration = GpuTestConfiguration::new() @@ -245,7 +251,7 @@ async fn binding_array_samplers(ctx: TestingContext, partially_bound: bool) { ctx.queue.submit(Some(encoder.finish())); readback_buffer.slice(..).map_async(MapMode::Read, |_| {}); - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); let readback_buffer_slice = readback_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/binding_array/storage_textures.rs b/tests/tests/wgpu-gpu/binding_array/storage_textures.rs index 1118247551d..69cec788c4f 100644 --- a/tests/tests/wgpu-gpu/binding_array/storage_textures.rs +++ b/tests/tests/wgpu-gpu/binding_array/storage_textures.rs @@ -2,10 +2,17 @@ use std::num::NonZeroU32; use wgpu::*; use wgpu_test::{ - gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters, - TestingContext, + gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, GpuTestInitializer, + TestParameters, TestingContext, }; +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + BINDING_ARRAY_STORAGE_TEXTURES, + PARTIAL_BINDING_ARRAY_STORAGE_TEXTURES, + ]); +} + #[gpu_test] static BINDING_ARRAY_STORAGE_TEXTURES: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( diff --git a/tests/tests/wgpu-gpu/buffer.rs b/tests/tests/wgpu-gpu/buffer.rs index 463820d48c2..741b34bb3bc 100644 --- a/tests/tests/wgpu-gpu/buffer.rs +++ b/tests/tests/wgpu-gpu/buffer.rs @@ -1,4 +1,17 @@ -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + EMPTY_BUFFER, + MAP_OFFSET, + MINIMUM_BUFFER_BINDING_SIZE_LAYOUT, + MINIMUM_BUFFER_BINDING_SIZE_DISPATCH, + CLEAR_OFFSET_OUTSIDE_RESOURCE_BOUNDS, + CLEAR_OFFSET_PLUS_SIZE_OUTSIDE_U64_BOUNDS, + ]); +} async fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: &str) { let r = wgpu::BufferUsages::MAP_READ; @@ -14,7 +27,9 @@ async fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: b0.slice(0..0) .map_async(wgpu::MapMode::Read, Result::unwrap); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); { let view = b0.slice(0..0).get_mapped_range(); @@ -48,7 +63,9 @@ async fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: b0.slice(0..0) .map_async(wgpu::MapMode::Write, Result::unwrap); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); //{ // let view = b0.slice(0..0).get_mapped_range_mut(); @@ -77,83 +94,95 @@ async fn test_empty_buffer_range(ctx: &TestingContext, buffer_size: u64, label: b1.unmap(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } #[gpu_test] static EMPTY_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().expect_fail(FailureCase::always())) + .parameters( + TestParameters::default() + .expect_fail(FailureCase::always()) + .enable_noop(), + ) .run_async(|ctx| async move { test_empty_buffer_range(&ctx, 2048, "regular buffer").await; test_empty_buffer_range(&ctx, 0, "zero-sized buffer").await; }); #[gpu_test] -static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_async(|ctx| async move { - // This test writes 16 bytes at the beginning of buffer mapped mapped with - // an offset of 32 bytes. Then the buffer is copied into another buffer that - // is read back and we check that the written bytes are correctly placed at - // offset 32..48. - // The goal is to check that get_mapped_range did not accidentally double-count - // the mapped offset. - - let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 256, - usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, - mapped_at_creation: false, - }); - let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 256, - usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); - - write_buf - .slice(32..) - .map_async(wgpu::MapMode::Write, move |result| { - result.unwrap(); +static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { + // This test writes 16 bytes at the beginning of buffer mapped mapped with + // an offset of 32 bytes. Then the buffer is copied into another buffer that + // is read back and we check that the written bytes are correctly placed at + // offset 32..48. + // The goal is to check that get_mapped_range did not accidentally double-count + // the mapped offset. + + let write_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 256, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + let read_buf = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 256, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, }); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + write_buf + .slice(32..) + .map_async(wgpu::MapMode::Write, move |result| { + result.unwrap(); + }); - { - let slice = write_buf.slice(32..48); - let mut view = slice.get_mapped_range_mut(); - for byte in &mut view[..] { - *byte = 2; + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); + + { + let slice = write_buf.slice(32..48); + let mut view = slice.get_mapped_range_mut(); + for byte in &mut view[..] { + *byte = 2; + } } - } - write_buf.unmap(); + write_buf.unmap(); - let mut encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256); + encoder.copy_buffer_to_buffer(&write_buf, 0, &read_buf, 0, 256); - ctx.queue.submit(Some(encoder.finish())); + ctx.queue.submit(Some(encoder.finish())); - read_buf - .slice(..) - .map_async(wgpu::MapMode::Read, Result::unwrap); + read_buf + .slice(..) + .map_async(wgpu::MapMode::Read, Result::unwrap); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); - let slice = read_buf.slice(..); - let view = slice.get_mapped_range(); - for byte in &view[0..32] { - assert_eq!(*byte, 0); - } - for byte in &view[32..48] { - assert_eq!(*byte, 2); - } - for byte in &view[48..] { - assert_eq!(*byte, 0); - } -}); + let slice = read_buf.slice(..); + let view = slice.get_mapped_range(); + for byte in &view[0..32] { + assert_eq!(*byte, 0); + } + for byte in &view[32..48] { + assert_eq!(*byte, 2); + } + for byte in &view[48..] { + assert_eq!(*byte, 0); + } + }); /// The WebGPU algorithm [validating shader binding][vsb] requires /// implementations to check that buffer bindings are large enough to @@ -164,7 +193,7 @@ static MAP_OFFSET: GpuTestConfiguration = GpuTestConfiguration::new().run_async( /// 16 for that variable's group/index. Pipeline creation should fail. #[gpu_test] static MINIMUM_BUFFER_BINDING_SIZE_LAYOUT: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters(TestParameters::default().test_features_limits().enable_noop()) .run_sync(|ctx| { // Create a shader module that statically uses a storage buffer. let shader_module = ctx @@ -234,7 +263,7 @@ static MINIMUM_BUFFER_BINDING_SIZE_LAYOUT: GpuTestConfiguration = GpuTestConfigu /// binding. Command recording should fail. #[gpu_test] static MINIMUM_BUFFER_BINDING_SIZE_DISPATCH: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters(TestParameters::default().test_features_limits().enable_noop()) .run_sync(|ctx| { // This test tries to use a bindgroup layout with a // min_binding_size of 16 to an index whose WGSL type requires 32 @@ -331,7 +360,7 @@ static MINIMUM_BUFFER_BINDING_SIZE_DISPATCH: GpuTestConfiguration = GpuTestConfi #[gpu_test] static CLEAR_OFFSET_OUTSIDE_RESOURCE_BOUNDS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let size = 16; @@ -357,7 +386,7 @@ static CLEAR_OFFSET_OUTSIDE_RESOURCE_BOUNDS: GpuTestConfiguration = GpuTestConfi #[gpu_test] static CLEAR_OFFSET_PLUS_SIZE_OUTSIDE_U64_BOUNDS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { label: None, diff --git a/tests/tests/wgpu-gpu/buffer_copy.rs b/tests/tests/wgpu-gpu/buffer_copy.rs index 8968d9227f7..a3091d21681 100644 --- a/tests/tests/wgpu-gpu/buffer_copy.rs +++ b/tests/tests/wgpu-gpu/buffer_copy.rs @@ -2,7 +2,11 @@ use wgpu::BufferAddress; -use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration}; +use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(COPY_ALIGNMENT); +} fn try_copy( ctx: &wgpu_test::TestingContext, @@ -22,49 +26,51 @@ fn try_copy( } #[gpu_test] -static COPY_ALIGNMENT: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - try_copy(&ctx, 0, 0, None); - try_copy( - &ctx, - 4, - 16 + 1, - Some("copy size 17 does not respect `copy_buffer_alignment`"), - ); - try_copy( - &ctx, - 64, - 20 + 2, - Some("copy size 22 does not respect `copy_buffer_alignment`"), - ); - try_copy( - &ctx, - 256, - 44 + 3, - Some("copy size 47 does not respect `copy_buffer_alignment`"), - ); - try_copy(&ctx, 1024, 8 + 4, None); +static COPY_ALIGNMENT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + try_copy(&ctx, 0, 0, None); + try_copy( + &ctx, + 4, + 16 + 1, + Some("copy size 17 does not respect `copy_buffer_alignment`"), + ); + try_copy( + &ctx, + 64, + 20 + 2, + Some("copy size 22 does not respect `copy_buffer_alignment`"), + ); + try_copy( + &ctx, + 256, + 44 + 3, + Some("copy size 47 does not respect `copy_buffer_alignment`"), + ); + try_copy(&ctx, 1024, 8 + 4, None); - try_copy(&ctx, 0, 4, None); - try_copy( - &ctx, - 4 + 1, - 8, - Some("buffer offset 5 is not aligned to block size or `copy_buffer_alignment`"), - ); - try_copy( - &ctx, - 64 + 2, - 12, - Some("buffer offset 66 is not aligned to block size or `copy_buffer_alignment`"), - ); - try_copy( - &ctx, - 256 + 3, - 16, - Some("buffer offset 259 is not aligned to block size or `copy_buffer_alignment`"), - ); - try_copy(&ctx, 1024 + 4, 4, None); -}); + try_copy(&ctx, 0, 4, None); + try_copy( + &ctx, + 4 + 1, + 8, + Some("buffer offset 5 is not aligned to block size or `copy_buffer_alignment`"), + ); + try_copy( + &ctx, + 64 + 2, + 12, + Some("buffer offset 66 is not aligned to block size or `copy_buffer_alignment`"), + ); + try_copy( + &ctx, + 256 + 3, + 16, + Some("buffer offset 259 is not aligned to block size or `copy_buffer_alignment`"), + ); + try_copy(&ctx, 1024 + 4, 4, None); + }); const BUFFER_SIZE: BufferAddress = 1234; diff --git a/tests/tests/wgpu-gpu/buffer_usages.rs b/tests/tests/wgpu-gpu/buffer_usages.rs index efb7636f65e..b0a65583b7a 100644 --- a/tests/tests/wgpu-gpu/buffer_usages.rs +++ b/tests/tests/wgpu-gpu/buffer_usages.rs @@ -2,7 +2,17 @@ use wgpu::BufferAddress; use wgpu::{BufferUsages as Bu, MapMode as Ma}; -use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + fail_if, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + BUFFER_USAGE, + BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS, + BUFFER_MAP_ASYNC_MAP_STATE, + ]); +} const BUFFER_SIZE: BufferAddress = 1234; @@ -49,20 +59,26 @@ fn try_create(ctx: TestingContext, usages: &[(bool, &[wgpu::BufferUsages])]) { } #[gpu_test] -static BUFFER_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - try_create( - ctx, - &[ - (false, ALWAYS_VALID), - (true, NEEDS_MAPPABLE_PRIMARY_BUFFERS), - (true, ALWAYS_FAIL), - ], - ); -}); +static BUFFER_USAGE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + try_create( + ctx, + &[ + (false, ALWAYS_VALID), + (true, NEEDS_MAPPABLE_PRIMARY_BUFFERS), + (true, ALWAYS_FAIL), + ], + ); + }); #[gpu_test] static BUFFER_USAGE_MAPPABLE_PRIMARY_BUFFERS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS)) + .parameters( + TestParameters::default() + .features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS) + .enable_noop(), + ) .run_sync(|ctx| { try_create( ctx, @@ -83,7 +99,7 @@ async fn map_test( after_unmap: bool, after_destroy: bool, ) { - log::info!("map_test usage_type:{usage_type} map_mode_type:{:?} before_unmap:{before_unmap} before_destroy:{before_destroy} after_unmap:{after_unmap} after_destroy:{after_destroy}", map_mode_type); + log::info!("map_test usage_type:{usage_type} map_mode_type:{map_mode_type:?} before_unmap:{before_unmap} before_destroy:{before_destroy} after_unmap:{after_unmap} after_destroy:{after_destroy}"); let size = 8; let usage = match usage_type { @@ -139,7 +155,9 @@ async fn map_test( buffer.destroy(); } - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); if !before_unmap && !before_destroy { { @@ -159,7 +177,11 @@ async fn map_test( #[gpu_test] static BUFFER_MAP_ASYNC_MAP_STATE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS)) + .parameters( + TestParameters::default() + .features(wgpu::Features::MAPPABLE_PRIMARY_BUFFERS) + .enable_noop(), + ) .run_async(move |ctx| async move { for usage_type in ["invalid", "read", "write"] { for map_mode_type in [Ma::Read, Ma::Write] { diff --git a/tests/tests/wgpu-gpu/clear_texture.rs b/tests/tests/wgpu-gpu/clear_texture.rs index 0789eedb1cc..aee78b2a07e 100644 --- a/tests/tests/wgpu-gpu/clear_texture.rs +++ b/tests/tests/wgpu-gpu/clear_texture.rs @@ -1,8 +1,20 @@ use wgpu_test::{ - gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters, - TestingContext, + gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, GpuTestInitializer, + TestParameters, TestingContext, }; +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + CLEAR_TEXTURE_UNCOMPRESSED_GLES, + CLEAR_TEXTURE_UNCOMPRESSED, + CLEAR_TEXTURE_DEPTH, + CLEAR_TEXTURE_DEPTH32_STENCIL8, + CLEAR_TEXTURE_COMPRESSED_BCN, + CLEAR_TEXTURE_COMPRESSED_ASTC, + CLEAR_TEXTURE_COMPRESSED_ETC2, + ]); +} + static TEXTURE_FORMATS_UNCOMPRESSED_GLES_COMPAT: &[wgpu::TextureFormat] = &[ wgpu::TextureFormat::R8Unorm, wgpu::TextureFormat::R8Snorm, @@ -209,12 +221,7 @@ async fn single_texture_clear_test( size: wgpu::Extent3d, dimension: wgpu::TextureDimension, ) { - log::info!( - "clearing texture with {:?}, dimension {:?}, size {:?}", - format, - dimension, - size - ); + log::info!("clearing texture with {format:?}, dimension {dimension:?}, size {size:?}"); let extra_usages = match format { wgpu::TextureFormat::Depth24Plus | wgpu::TextureFormat::Depth24PlusStencil8 => { diff --git a/tests/tests/wgpu-gpu/clip_distances.rs b/tests/tests/wgpu-gpu/clip_distances.rs index 26961d7e098..bbdd2d539ff 100644 --- a/tests/tests/wgpu-gpu/clip_distances.rs +++ b/tests/tests/wgpu-gpu/clip_distances.rs @@ -1,4 +1,10 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(CLIP_DISTANCES); +} #[gpu_test] static CLIP_DISTANCES: GpuTestConfiguration = GpuTestConfiguration::new() @@ -117,7 +123,9 @@ async fn clip_distances(ctx: TestingContext) { ctx.queue.submit([encoder.finish()]); let slice = readback_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data: &[u8] = &slice.get_mapped_range(); // We should have filled the upper sector of the texture. Verify that this is the case. diff --git a/tests/tests/wgpu-gpu/cloneable_types.rs b/tests/tests/wgpu-gpu/cloneable_types.rs index b38fbbd2964..fd578f6cc92 100644 --- a/tests/tests/wgpu-gpu/cloneable_types.rs +++ b/tests/tests/wgpu-gpu/cloneable_types.rs @@ -1,8 +1,13 @@ -use wgpu_test::{gpu_test, TestingContext}; +use wgpu_test::{gpu_test, GpuTestInitializer, TestParameters, TestingContext}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(CLONEABLE_BUFFERS); +} #[gpu_test] -static CLONEABLE_BUFFERS: GpuTestConfiguration = - wgpu_test::GpuTestConfiguration::new().run_sync(cloneable_buffers); +static CLONEABLE_BUFFERS: GpuTestConfiguration = wgpu_test::GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(cloneable_buffers); // Test a basic case of cloneable types where you clone the buffer to be able // to access the buffer inside the callback as well as outside. @@ -35,7 +40,9 @@ fn cloneable_buffers(ctx: TestingContext) { assert_eq!(&*data, &cloned_buffer_contents); }); - ctx.device.poll(wgpu::PollType::Wait).unwrap(); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); let data = buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/compute_pass_ownership.rs b/tests/tests/wgpu-gpu/compute_pass_ownership.rs index 168ad8bd789..48f3ee4197c 100644 --- a/tests/tests/wgpu-gpu/compute_pass_ownership.rs +++ b/tests/tests/wgpu-gpu/compute_pass_ownership.rs @@ -4,7 +4,18 @@ use std::num::NonZeroU64; use wgpu::util::DeviceExt as _; -use wgpu_test::{gpu_test, valid, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + COMPUTE_PASS_RESOURCE_OWNERSHIP, + COMPUTE_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS, + COMPUTE_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS, + COMPUTE_PASS_KEEP_ENCODER_ALIVE, + ]); +} const SHADER_SRC: &str = " @group(0) @binding(0) @@ -52,7 +63,9 @@ async fn compute_pass_resource_ownership(ctx: TestingContext) { drop(pipeline); drop(bind_group); drop(indirect_buffer); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } assert_compute_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await; @@ -100,7 +113,9 @@ async fn compute_pass_query_set_ownership_pipeline_statistics(ctx: TestingContex // Drop the query set. Then do a device poll to make sure it's not dropped too early, no matter what. drop(query_set); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } assert_compute_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await; @@ -156,7 +171,9 @@ async fn compute_pass_query_set_ownership_timestamps(ctx: TestingContext) { // Drop the query sets. Then do a device poll to make sure they're not dropped too early, no matter what. drop(query_set_timestamp_writes); drop(query_set_write_timestamp); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } assert_compute_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await; @@ -164,7 +181,11 @@ async fn compute_pass_query_set_ownership_timestamps(ctx: TestingContext) { #[gpu_test] static COMPUTE_PASS_KEEP_ENCODER_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_async(compute_pass_keep_encoder_alive); async fn compute_pass_keep_encoder_alive(ctx: TestingContext) { @@ -191,7 +212,9 @@ async fn compute_pass_keep_encoder_alive(ctx: TestingContext) { let mut cpass = cpass.forget_lifetime(); drop(encoder); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); // Record some draw commands. cpass.set_pipeline(&pipeline); @@ -215,7 +238,9 @@ async fn assert_compute_pass_executed_normally( encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size); ctx.queue.submit([encoder.finish()]); cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = cpu_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/device.rs b/tests/tests/wgpu-gpu/device.rs index 53904d2e182..7e9d4204ccd 100644 --- a/tests/tests/wgpu-gpu/device.rs +++ b/tests/tests/wgpu-gpu/device.rs @@ -1,10 +1,36 @@ use std::sync::atomic::AtomicBool; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + CROSS_DEVICE_BIND_GROUP_USAGE, + DEVICE_DESTROY_THEN_MORE, + DEVICE_DESTROY_THEN_LOST, + DIFFERENT_BGL_ORDER_BW_SHADER_AND_API, + DEVICE_DESTROY_THEN_BUFFER_CLEANUP, + DEVICE_AND_QUEUE_HAVE_DIFFERENT_IDS, + ]); + + #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] + { + vec.extend([ + DEVICE_LIFETIME_CHECK, + MULTIPLE_DEVICES, + REQUEST_DEVICE_ERROR_MESSAGE_NATIVE, + ]); + } +} #[gpu_test] static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().expect_fail(FailureCase::always())) + .parameters( + TestParameters::default() + .expect_fail(FailureCase::always()) + .enable_noop(), + ) .run_async(|ctx| async move { // Create a bind group using a layout from another device. This should be a validation // error but currently crashes. @@ -31,7 +57,7 @@ static CROSS_DEVICE_BIND_GROUP_USAGE: GpuTestConfiguration = GpuTestConfiguratio #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] #[gpu_test] static DEVICE_LIFETIME_CHECK: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { ctx.instance.poll_all(false); @@ -58,7 +84,7 @@ static DEVICE_LIFETIME_CHECK: GpuTestConfiguration = GpuTestConfiguration::new() #[cfg(not(all(target_arch = "wasm32", not(target_os = "emscripten"))))] #[gpu_test] static MULTIPLE_DEVICES: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { use pollster::FutureExt as _; ctx.adapter @@ -130,7 +156,7 @@ async fn request_device_error_message() { let expected = "TypeError"; } else { // This message appears whenever wgpu-core is used as the implementation. - let expected = "Unsupported features were requested: Features {"; + let expected = "Unsupported features were requested:"; } } assert!(device_error.contains(expected), "{device_error}"); @@ -140,7 +166,11 @@ async fn request_device_error_message() { // should turn into no-ops, per spec. #[gpu_test] static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::CLEAR_TEXTURE)) + .parameters( + TestParameters::default() + .features(wgpu::Features::CLEAR_TEXTURE) + .enable_noop(), + ) .run_sync(|ctx| { // Create some resources on the device that we will attempt to use *after* losing // the device. @@ -452,7 +482,7 @@ static DEVICE_DESTROY_THEN_MORE: GpuTestConfiguration = GpuTestConfiguration::ne #[gpu_test] static DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_async(|ctx| async move { // This test checks that when device.destroy is called, the provided // DeviceLostClosure is called with reason DeviceLostReason::Destroyed. @@ -474,7 +504,7 @@ static DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::ne // Make sure the device queues are empty, which ensures that the closure // has been called. assert!(ctx - .async_poll(wgpu::PollType::wait()) + .async_poll(wgpu::PollType::wait_indefinitely()) .await .unwrap() .is_queue_empty()); @@ -487,7 +517,7 @@ static DEVICE_DESTROY_THEN_LOST: GpuTestConfiguration = GpuTestConfiguration::ne #[gpu_test] static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { // This test addresses a bug found in multiple backends where `wgpu_core` and `wgpu_hal` // backends made different assumptions about the element order of vectors of bind group @@ -614,7 +644,7 @@ static DIFFERENT_BGL_ORDER_BW_SHADER_AND_API: GpuTestConfiguration = GpuTestConf #[gpu_test] static DEVICE_DESTROY_THEN_BUFFER_CLEANUP: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { // When a device is destroyed, its resources should be released, // without causing a deadlock. @@ -653,7 +683,7 @@ static DEVICE_DESTROY_THEN_BUFFER_CLEANUP: GpuTestConfiguration = GpuTestConfigu #[gpu_test] static DEVICE_AND_QUEUE_HAVE_DIFFERENT_IDS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_async(|ctx| async move { let TestingContext { adapter, diff --git a/tests/tests/wgpu-gpu/dispatch_workgroups_indirect.rs b/tests/tests/wgpu-gpu/dispatch_workgroups_indirect.rs index c915abf75ac..f665440fb4a 100644 --- a/tests/tests/wgpu-gpu/dispatch_workgroups_indirect.rs +++ b/tests/tests/wgpu-gpu/dispatch_workgroups_indirect.rs @@ -1,4 +1,15 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + NUM_WORKGROUPS_BUILTIN, + DISCARD_DISPATCH, + RESET_BIND_GROUPS, + ZERO_SIZED_BUFFER, + ]); +} /// Make sure that the num_workgroups builtin works properly (it requires a workaround on D3D12). #[gpu_test] @@ -63,7 +74,7 @@ static RESET_BIND_GROUPS: GpuTestConfiguration = GpuTestConfiguration::new() .limits(wgpu::Limits { max_push_constant_size: 4, ..wgpu::Limits::downlevel_defaults() - }), + }).enable_noop(), ) .run_async(|ctx| async move { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -105,7 +116,8 @@ static ZERO_SIZED_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new() .limits(wgpu::Limits { max_push_constant_size: 4, ..wgpu::Limits::downlevel_defaults() - }), + }) + .enable_noop(), ) .run_async(|ctx| async move { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -300,7 +312,9 @@ async fn run_test(ctx: &TestingContext, num_workgroups: &[u32; 3]) -> [u32; 3] { .slice(..) .map_async(wgpu::MapMode::Read, |_| {}); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let view = test_resources.readback_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/draw_indirect.rs b/tests/tests/wgpu-gpu/draw_indirect.rs index ac88c43bc71..e33ccb83ab7 100644 --- a/tests/tests/wgpu-gpu/draw_indirect.rs +++ b/tests/tests/wgpu-gpu/draw_indirect.rs @@ -2,7 +2,33 @@ use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, vertex_attr_array, }; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend(&[ + DRAW, + DRAW_OOB_START, + DRAW_OOB_COUNT, + INSTANCED_DRAW, + INSTANCED_DRAW_OOB_START, + INSTANCED_DRAW_OOB_COUNT, + INSTANCED_DRAW_OOB_INSTANCE_START, + INSTANCED_DRAW_OOB_INSTANCE_COUNT, + INSTANCED_DRAW_WITH_NON_ZERO_FIRST_INSTANCE, + INSTANCED_DRAW_WITH_NON_ZERO_FIRST_INSTANCE_MISSING_FEATURE, + INDEXED_DRAW, + INDEXED_DRAW_OOB_START, + INDEXED_DRAW_OOB_COUNT, + INSTANCED_INDEXED_DRAW, + INSTANCED_INDEXED_DRAW_OOB_START, + INSTANCED_INDEXED_DRAW_OOB_COUNT, + INSTANCED_INDEXED_DRAW_OOB_INSTANCE_START, + INSTANCED_INDEXED_DRAW_OOB_INSTANCE_COUNT, + INDIRECT_BUFFER_OFFSETS, + ]); +} struct TestData { kind: Kind, @@ -296,7 +322,9 @@ async fn run_test(ctx: TestingContext, test_data: TestData, expect_noop: bool) { let slice = readback_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = slice.get_mapped_range(); let succeeded = if expect_noop { @@ -317,12 +345,18 @@ macro_rules! make_test { ($name:ident, $test_data:expr, $expect_noop:expr, $features:expr) => { #[gpu_test] static $name: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters( - TestParameters::default() + .parameters({ + let params = TestParameters::default() .downlevel_flags(wgpu::DownlevelFlags::INDIRECT_EXECUTION) .features($features) - .limits(wgpu::Limits::downlevel_defaults()), - ) + .limits(wgpu::Limits::downlevel_defaults()); + + if $expect_noop { + params.enable_noop() + } else { + params + } + }) .run_async(|ctx| run_test(ctx, $test_data, $expect_noop)); }; } @@ -750,7 +784,9 @@ async fn indirect_buffer_offsets(ctx: TestingContext) { let slice = readback_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = slice.get_mapped_range(); let half = data.len() / 2; diff --git a/tests/tests/wgpu-gpu/dual_source_blending.rs b/tests/tests/wgpu-gpu/dual_source_blending.rs index 997cdd1a32f..eeffa72e524 100644 --- a/tests/tests/wgpu-gpu/dual_source_blending.rs +++ b/tests/tests/wgpu-gpu/dual_source_blending.rs @@ -1,5 +1,14 @@ use wgpu::*; -use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + DUAL_SOURCE_BLENDING_FEATURE_DISABLED, + DUAL_SOURCE_BLENDING_FEATURE_ENABLED, + ]); +} const VERTEX_SHADER: &str = r#" @vertex @@ -46,7 +55,7 @@ fn blend_state_with_dual_source_blending() -> BlendState { #[gpu_test] static DUAL_SOURCE_BLENDING_FEATURE_DISABLED: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_async(dual_source_blending_disabled); async fn dual_source_blending_disabled(ctx: TestingContext) { @@ -109,7 +118,11 @@ async fn dual_source_blending_disabled(ctx: TestingContext) { #[gpu_test] static DUAL_SOURCE_BLENDING_FEATURE_ENABLED: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::DUAL_SOURCE_BLENDING)) + .parameters( + TestParameters::default() + .features(wgpu::Features::DUAL_SOURCE_BLENDING) + .enable_noop(), + ) .run_async(dual_source_blending_enabled); async fn dual_source_blending_enabled(ctx: TestingContext) { diff --git a/tests/tests/wgpu-gpu/encoder.rs b/tests/tests/wgpu-gpu/encoder.rs index aa91173130f..426ef3c808e 100644 --- a/tests/tests/wgpu-gpu/encoder.rs +++ b/tests/tests/wgpu-gpu/encoder.rs @@ -1,21 +1,37 @@ use wgpu::util::DeviceExt; use wgpu::CommandEncoder; use wgpu_test::{ - fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext, + fail, gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, + TestingContext, }; +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + DROP_ENCODER, + DROP_QUEUE_BEFORE_CREATING_COMMAND_ENCODER, + DROP_ENCODER_AFTER_ERROR, + ENCODER_OPERATIONS_FAIL_WHILE_PASS_ALIVE, + ]); +} + #[gpu_test] -static DROP_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - let encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - drop(encoder); -}); +static DROP_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + let encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + drop(encoder); + }); #[gpu_test] static DROP_QUEUE_BEFORE_CREATING_COMMAND_ENCODER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().expect_fail(FailureCase::always())) + .parameters( + TestParameters::default() + .expect_fail(FailureCase::always()) + .enable_noop(), + ) .run_sync(|ctx| { // Use the device after the queue is dropped. Currently this panics // but it probably shouldn't. @@ -28,7 +44,7 @@ static DROP_QUEUE_BEFORE_CREATING_COMMAND_ENCODER: GpuTestConfiguration = #[gpu_test] static DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let mut encoder = ctx .device @@ -72,11 +88,15 @@ static DROP_ENCODER_AFTER_ERROR: GpuTestConfiguration = GpuTestConfiguration::ne #[gpu_test] static ENCODER_OPERATIONS_FAIL_WHILE_PASS_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features( - wgpu::Features::CLEAR_TEXTURE - | wgpu::Features::TIMESTAMP_QUERY - | wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, - )) + .parameters( + TestParameters::default() + .features( + wgpu::Features::CLEAR_TEXTURE + | wgpu::Features::TIMESTAMP_QUERY + | wgpu::Features::TIMESTAMP_QUERY_INSIDE_ENCODERS, + ) + .enable_noop(), + ) .run_sync(encoder_operations_fail_while_pass_alive); fn encoder_operations_fail_while_pass_alive(ctx: TestingContext) { diff --git a/tests/tests/wgpu-gpu/external_texture.rs b/tests/tests/wgpu-gpu/external_image_copy.rs similarity index 99% rename from tests/tests/wgpu-gpu/external_texture.rs rename to tests/tests/wgpu-gpu/external_image_copy.rs index 78d95882608..044e2632425 100644 --- a/tests/tests/wgpu-gpu/external_texture.rs +++ b/tests/tests/wgpu-gpu/external_image_copy.rs @@ -328,7 +328,9 @@ static IMAGE_BITMAP_IMPORT: GpuTestConfiguration = readback_buffer .slice(..) .map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let buffer = readback_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/external_texture/dimensions.wgsl b/tests/tests/wgpu-gpu/external_texture/dimensions.wgsl new file mode 100644 index 00000000000..422d676e3d5 --- /dev/null +++ b/tests/tests/wgpu-gpu/external_texture/dimensions.wgsl @@ -0,0 +1,10 @@ +@group(0) @binding(0) +var tex: texture_external; + +@group(0) @binding(1) +var output: vec2; + +@compute @workgroup_size(1) +fn main(@builtin(global_invocation_id) global_id: vec3) { + output = textureDimensions(tex); +} diff --git a/tests/tests/wgpu-gpu/external_texture/load.wgsl b/tests/tests/wgpu-gpu/external_texture/load.wgsl new file mode 100644 index 00000000000..b9f28d679fa --- /dev/null +++ b/tests/tests/wgpu-gpu/external_texture/load.wgsl @@ -0,0 +1,12 @@ +@group(0) @binding(0) +var tex: texture_external; + +@group(0) @binding(1) +var coords: array>; +@group(0) @binding(2) +var output: array>; + +@compute @workgroup_size(1) +fn main(@builtin(global_invocation_id) id: vec3) { + output[id.x] = textureLoad(tex, coords[id.x]); +} diff --git a/tests/tests/wgpu-gpu/external_texture/mod.rs b/tests/tests/wgpu-gpu/external_texture/mod.rs new file mode 100644 index 00000000000..7df1cc1d648 --- /dev/null +++ b/tests/tests/wgpu-gpu/external_texture/mod.rs @@ -0,0 +1,1127 @@ +use approx::assert_abs_diff_eq; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + EXTERNAL_TEXTURE_DIMENSIONS, + EXTERNAL_TEXTURE_LOAD, + EXTERNAL_TEXTURE_LOAD_YUV, + EXTERNAL_TEXTURE_LOAD_TRANSFORM, + EXTERNAL_TEXTURE_LOAD_INVALID_ADDRESS, + EXTERNAL_TEXTURE_SAMPLE, + EXTERNAL_TEXTURE_SAMPLE_YUV, + EXTERNAL_TEXTURE_SAMPLE_TRANSFORM, + ]); +} + +// Input texture data +const RED_U8: [u8; 4] = [0xFF, 0x00, 0x00, 0xFF]; +const GREEN_U8: [u8; 4] = [0x00, 0xFF, 0x00, 0xFF]; +const BLUE_U8: [u8; 4] = [0x00, 0x00, 0xFF, 0xFF]; +const YELLOW_U8: [u8; 4] = [0xFF, 0xFF, 0x00, 0xFF]; +const BLACK_U8: [u8; 4] = [0x00, 0x00, 0x00, 0xFF]; + +// Data for a 2x2 or 4x1 texture with red, green, blue, and yellow pixels +const RGBA_TEXTURE_DATA: [[u8; 4]; 4] = [RED_U8, GREEN_U8, BLUE_U8, YELLOW_U8]; + +// Data for a 4x4 texture with red, green, blue, and yellow pixels in the +// centre, surrounded by a black border. +#[rustfmt::skip] +const RGBA_TEXTURE_DATA_WITH_BORDER: [[u8; 4]; 16] = [ + BLACK_U8, BLACK_U8, BLACK_U8, BLACK_U8, + BLACK_U8, RED_U8, GREEN_U8, BLACK_U8, + BLACK_U8, BLUE_U8, YELLOW_U8, BLACK_U8, + BLACK_U8, BLACK_U8, BLACK_U8, BLACK_U8, +]; + +// Red, green, blue, and yellow in BT601 limited range YUV. Values obtained by +// extracting raw frames from the WebGPU CTS' "four-colors" video [1], and +// inspecting the contents. +// [1] https://github.com/gpuweb/cts/blob/44dac855ba07b23c49d2cbc2c9d87bf8e6f38c47/src/resources/four-colors-vp8-bt601.webm +const RED_Y_U8: u8 = 0x51; +const RED_U_U8: u8 = 0x5A; +const RED_V_U8: u8 = 0xF0; +const GREEN_Y_U8: u8 = 0x91; +const GREEN_U_U8: u8 = 0x35; +const GREEN_V_U8: u8 = 0x22; +const BLUE_Y_U8: u8 = 0x29; +const BLUE_U_U8: u8 = 0xF0; +const BLUE_V_U8: u8 = 0x6E; +const YELLOW_Y_U8: u8 = 0xD2; +const YELLOW_U_U8: u8 = 0x10; +const YELLOW_V_U8: u8 = 0x92; + +// Data for a 4x4 texture with 4:2:0 chroma subsampling. The top-left quadrant +// is red, top-right green, bottom-left blue, and bottom-right yellow. +#[rustfmt::skip] +const Y_TEXTURE_DATA: [u8; 16] = [ + RED_Y_U8, RED_Y_U8, GREEN_Y_U8, GREEN_Y_U8, + RED_Y_U8, RED_Y_U8, GREEN_Y_U8, GREEN_Y_U8, + BLUE_Y_U8, BLUE_Y_U8, YELLOW_Y_U8, YELLOW_Y_U8, + BLUE_Y_U8, BLUE_Y_U8, YELLOW_Y_U8, YELLOW_Y_U8, +]; +const U_TEXTURE_DATA: [u8; 4] = [RED_U_U8, GREEN_U_U8, BLUE_U_U8, YELLOW_U_U8]; +const V_TEXTURE_DATA: [u8; 4] = [RED_V_U8, GREEN_V_U8, BLUE_V_U8, YELLOW_V_U8]; + +// Expected results after texture load/sample. +const RED_F32: [f32; 4] = [1.0, 0.0, 0.0, 1.0]; +const GREEN_F32: [f32; 4] = [0.0, 1.0, 0.0, 1.0]; +const BLUE_F32: [f32; 4] = [0.0, 0.0, 1.0, 1.0]; +const YELLOW_F32: [f32; 4] = [1.0, 1.0, 0.0, 1.0]; +const OPAQUE_BLACK_F32: [f32; 4] = [0.0, 0.0, 0.0, 1.0]; +const TRANSPARENT_BLACK_F32: [f32; 4] = [0.0, 0.0, 0.0, 0.0]; + +// Expected results after texture load/sample in sRGB color space. Values +// taken from the WebGPU CTS: +// https://github.com/gpuweb/cts/blob/44dac855ba07b23c49d2cbc2c9d87bf8e6f38c47/src/webgpu/web_platform/util.ts#L36-L43 +const RED_SRGB_F32: [f32; 4] = [0.9729456, 0.14179438, -0.020958992, 1.0]; +const GREEN_SRGB_F32: [f32; 4] = [0.24823427, 0.98481035, -0.056470133, 1.0]; +const BLUE_SRGB_F32: [f32; 4] = [0.10159736, 0.13545112, 1.0026299, 1.0]; +const YELLOW_SRGB_F32: [f32; 4] = [0.99547076, 0.9927421, -0.07742912, 1.0]; + +#[rustfmt::skip] +const IDENTITY_YUV_CONVERSION_MATRIX: [f32; 16] = [ + 1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0, +]; +#[rustfmt::skip] +const BT601_YUV_CONVERSION_MATRIX: [f32; 16] = [ + 1.1643835, 1.1643834, 1.1643835, 0.0, + 0.0, -0.39176223, 2.017232, 0.0, + 1.5960265, -0.81296754, 0.0, 0.0, + -0.8742022, 0.5316678, -1.0856307, 1.0 +]; + +const SRGB_TRANSFER_FUNCTION: wgpu::ExternalTextureTransferFunction = + wgpu::ExternalTextureTransferFunction { + a: 1.055, + b: 0.003130805, + g: 2.4, + k: 12.92, + }; + +const IDENTITY_GAMUT_CONVERSION_MATRIX: [f32; 9] = [1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0]; +#[rustfmt::skip] +const BT601_TO_SRGB_GAMUT_CONVERSION_MATRIX: [f32; 9] = [ + 0.93954253, 0.017772198, -0.0016215984, + 0.050181333, 0.96579295, -0.0043697506, + 0.010276437, 0.016434949, 1.0059911, +]; + +const IDENTITY_SAMPLE_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]; +const IDENTITY_LOAD_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, 1.0, 0.0, 0.0]; +// Flips a 2x2 texture horizontally +const HORIZONTAL_FLIP_2X2_SAMPLE_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, 1.0, 1.0, 0.0]; +const HORIZONTAL_FLIP_2X2_LOAD_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, 1.0, 1.0, 0.0]; +// Flips a 2x2 texture vertically +const VERTICAL_FLIP_2X2_SAMPLE_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, -1.0, 0.0, 1.0]; +const VERTICAL_FLIP_2X2_LOAD_TRANSFORM: [f32; 6] = [1.0, 0.0, 0.0, -1.0, 0.0, 1.0]; +// Rotates a 4x1 texture 90 degrees +const ROTATE_90_4X1_SAMPLE_TRANSFORM: [f32; 6] = [0.0, -1.0, 1.0, 0.0, 0.0, 1.0]; +const ROTATE_90_4X1_LOAD_TRANSFORM: [f32; 6] = [0.0, 1.0, 1.0, 0.0, 0.0, 0.0]; +// Rotates a 4x1 texture 180 degrees +const ROTATE_180_4X1_SAMPLE_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, -1.0, 1.0, 1.0]; +const ROTATE_180_4X1_LOAD_TRANSFORM: [f32; 6] = [-1.0, 0.0, 0.0, 0.0, 3.0, 0.0]; +// Rotates a 4xx1 texture 270 degrees +const ROTATE_270_4X1_SAMPLE_TRANSFORM: [f32; 6] = [0.0, 1.0, -1.0, 0.0, 1.0, 0.0]; +const ROTATE_270_4X1_LOAD_TRANSFORM: [f32; 6] = [0.0, 0.0, -1.0, 0.0, 3.0, 0.0]; +// Crops the middle 2x2 pixels from a 4x4 texture +const CROP_4X4_SAMPLE_TRANSFORM: [f32; 6] = [0.5, 0.0, 0.0, 0.5, 0.25, 0.25]; +const CROP_4X4_LOAD_TRANSFORM: [f32; 6] = [0.5, 0.0, 0.0, 0.5, 1.0, 1.0]; + +/// Helper function to create a 2D texture and a view, optionally writing the +/// provided data to the texture, and returning the view. +fn create_texture_and_view( + ctx: &TestingContext, + size: wgpu::Extent3d, + format: wgpu::TextureFormat, + data: Option<&[u8]>, +) -> wgpu::TextureView { + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::COPY_DST | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + if let Some(data) = data { + ctx.queue.write_texture( + texture.as_image_copy(), + data, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(size.width * format.components() as u32), + rows_per_image: None, + }, + size, + ); + } + texture.create_view(&wgpu::TextureViewDescriptor::default()) +} + +/// Helper function to perform textureDimensions() and return the result. +fn get_dimensions(ctx: &TestingContext, texture_resource: wgpu::BindingResource) -> [u32; 2] { + let module = ctx + .device + .create_shader_module(wgpu::include_wgsl!("dimensions.wgsl")); + let pipeline = ctx + .device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: None, + module: &module, + entry_point: Some("main"), + compilation_options: wgpu::PipelineCompilationOptions::default(), + cache: None, + }); + + let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: std::mem::size_of::<[u32; 2]>() as _, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + let download_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: output_buffer.size(), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: texture_resource, + }, + wgpu::BindGroupEntry { + binding: 1, + resource: output_buffer.as_entire_binding(), + }, + ], + }); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default()); + pass.set_pipeline(&pipeline); + pass.set_bind_group(0, &bind_group, &[]); + pass.dispatch_workgroups(1, 1, 1); + } + + encoder.copy_buffer_to_buffer(&output_buffer, 0, &download_buffer, 0, output_buffer.size()); + ctx.queue.submit(Some(encoder.finish())); + let buffer_slice = download_buffer.slice(..); + buffer_slice.map_async(wgpu::MapMode::Read, |_| {}); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); + + let data = buffer_slice.get_mapped_range(); + let size: &[u32] = bytemuck::cast_slice(&data); + size.try_into().unwrap() +} + +/// Helper function to perform `textureLoad()` for the specified coordinates and return +/// the loaded values. +fn get_loads( + ctx: &TestingContext, + coords: &[[u32; 2]], + texture_resource: wgpu::BindingResource, +) -> Vec<[f32; 4]> { + let module = ctx + .device + .create_shader_module(wgpu::include_wgsl!("load.wgsl")); + let pipeline = ctx + .device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: None, + module: &module, + entry_point: Some("main"), + compilation_options: wgpu::PipelineCompilationOptions::default(), + cache: None, + }); + + let coords_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: std::mem::size_of_val(coords) as _, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + ctx.queue + .write_buffer(&coords_buffer, 0, bytemuck::cast_slice(coords)); + + let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: (coords.len() * std::mem::size_of::<[f32; 4]>()) as _, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + let download_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: output_buffer.size(), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: texture_resource, + }, + wgpu::BindGroupEntry { + binding: 1, + resource: coords_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: output_buffer.as_entire_binding(), + }, + ], + }); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default()); + pass.set_pipeline(&pipeline); + pass.set_bind_group(0, &bind_group, &[]); + pass.dispatch_workgroups(coords.len() as _, 1, 1); + } + + encoder.copy_buffer_to_buffer(&output_buffer, 0, &download_buffer, 0, output_buffer.size()); + ctx.queue.submit(Some(encoder.finish())); + let buffer_slice = download_buffer.slice(..); + buffer_slice.map_async(wgpu::MapMode::Read, |_| {}); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); + + let data = buffer_slice.get_mapped_range(); + let values: &[[f32; 4]] = bytemuck::cast_slice(&data); + values.to_vec() +} + +/// Helper function to perform `textureSampleBaseClampToEdge()` for the specified +/// coordinates and return the sampled values. +fn get_samples( + ctx: &TestingContext, + coords: &[[f32; 2]], + texture_resource: wgpu::BindingResource, +) -> Vec<[f32; 4]> { + let module = ctx + .device + .create_shader_module(wgpu::include_wgsl!("sample.wgsl")); + let pipeline = ctx + .device + .create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: None, + layout: None, + module: &module, + entry_point: Some("main"), + compilation_options: wgpu::PipelineCompilationOptions::default(), + cache: None, + }); + + let coords_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: std::mem::size_of_val(coords) as _, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + ctx.queue + .write_buffer(&coords_buffer, 0, bytemuck::cast_slice(coords)); + + let output_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: (coords.len() * std::mem::size_of::<[f32; 4]>()) as _, + usage: wgpu::BufferUsages::STORAGE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + let download_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: output_buffer.size(), + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let sampler = ctx + .device + .create_sampler(&wgpu::SamplerDescriptor::default()); + + let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: texture_resource, + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: coords_buffer.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: output_buffer.as_entire_binding(), + }, + ], + }); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_compute_pass(&wgpu::ComputePassDescriptor::default()); + pass.set_pipeline(&pipeline); + pass.set_bind_group(0, &bind_group, &[]); + pass.dispatch_workgroups(coords.len() as _, 1, 1); + } + + encoder.copy_buffer_to_buffer(&output_buffer, 0, &download_buffer, 0, output_buffer.size()); + ctx.queue.submit(Some(encoder.finish())); + let buffer_slice = download_buffer.slice(..); + buffer_slice.map_async(wgpu::MapMode::Read, |_| {}); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); + + let data = buffer_slice.get_mapped_range(); + let values: &[[f32; 4]] = bytemuck::cast_slice(&data); + values.to_vec() +} + +/// Tests that `textureDimensions()` returns the correct value for both external textures +/// and texture views bound to an external texture binding. +#[gpu_test] +static EXTERNAL_TEXTURE_DIMENSIONS: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + const TEXTURE_WIDTH: u32 = 128; + const TEXTURE_HEIGHT: u32 = 64; + let view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: TEXTURE_WIDTH, + height: TEXTURE_HEIGHT, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + None, + ); + let dims = get_dimensions(&ctx, wgpu::BindingResource::TextureView(&view)); + assert_eq!(dims, [TEXTURE_WIDTH, TEXTURE_HEIGHT]); + + const EXTERNAL_TEXTURE_WIDTH: u32 = 32; + const EXTERNAL_TEXTURE_HEIGHT: u32 = 16; + let external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: EXTERNAL_TEXTURE_WIDTH, + height: EXTERNAL_TEXTURE_HEIGHT, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: IDENTITY_SAMPLE_TRANSFORM, + load_transform: IDENTITY_LOAD_TRANSFORM, + }, + &[&view], + ); + let dims = get_dimensions( + &ctx, + wgpu::BindingResource::ExternalTexture(&external_texture), + ); + // This should return the dimensions provided in the ExternalTextureDescriptor, + // rather than the dimensions of the underlying texture. + assert_eq!(dims, [EXTERNAL_TEXTURE_WIDTH, EXTERNAL_TEXTURE_HEIGHT]) + }); + +/// Tests that `textureLoad()` returns the correct values for both RGBA format external +/// textures and texture views bound to an external texture binding. +#[gpu_test] +static EXTERNAL_TEXTURE_LOAD: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + let view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [0, 1], [1, 1]], + wgpu::BindingResource::TextureView(&view), + ); + assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + + let external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: IDENTITY_SAMPLE_TRANSFORM, + load_transform: IDENTITY_LOAD_TRANSFORM, + }, + &[&view], + ); + + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [0, 1], [1, 1]], + wgpu::BindingResource::ExternalTexture(&external_texture), + ); + assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + }); + +/// Tests that `textureLoad()` returns the correct values for YUV format external +/// textures. +#[gpu_test] +static EXTERNAL_TEXTURE_LOAD_YUV: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + let y_view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::R8Unorm, + Some(&Y_TEXTURE_DATA), + ); + let u_view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::R8Unorm, + Some(&U_TEXTURE_DATA), + ); + let v_view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::R8Unorm, + Some(&V_TEXTURE_DATA), + ); + + let external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 4, + height: 4, + format: wgpu::ExternalTextureFormat::Yu12, + yuv_conversion_matrix: BT601_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: BT601_TO_SRGB_GAMUT_CONVERSION_MATRIX, + src_transfer_function: SRGB_TRANSFER_FUNCTION, + dst_transfer_function: SRGB_TRANSFER_FUNCTION, + sample_transform: IDENTITY_SAMPLE_TRANSFORM, + load_transform: IDENTITY_LOAD_TRANSFORM, + }, + &[&y_view, &u_view, &v_view], + ); + let loads = get_loads( + &ctx, + &[[1, 1], [2, 1], [1, 2], [2, 2]], + wgpu::BindingResource::ExternalTexture(&external_texture), + ); + + // `assert_abs_diff_eq!()` works for slices but not arrays, hence the + // following tomfoolery. + let loads = loads.iter().map(|arr| arr.as_slice()).collect::>(); + let expected = [RED_SRGB_F32, GREEN_SRGB_F32, BLUE_SRGB_F32, YELLOW_SRGB_F32]; + let expected = expected.each_ref().map(|arr| arr.as_slice()); + // We expect slight inaccuracies due to floating point maths. + assert_abs_diff_eq!(loads.as_slice(), expected.as_slice(), epsilon = 0.01); + }); + +/// Tests that `textureLoad()` returns the correct values for external textures with +/// various load transforms. +#[gpu_test] +static EXTERNAL_TEXTURE_LOAD_TRANSFORM: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + let view_2x2 = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + + let flip_h_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: HORIZONTAL_FLIP_2X2_SAMPLE_TRANSFORM, + load_transform: HORIZONTAL_FLIP_2X2_LOAD_TRANSFORM, + }, + &[&view_2x2], + ); + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [0, 1], [1, 1]], + wgpu::BindingResource::ExternalTexture(&flip_h_external_texture), + ); + assert_eq!(&loads, &[GREEN_F32, RED_F32, YELLOW_F32, BLUE_F32]); + + let flip_v_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: VERTICAL_FLIP_2X2_SAMPLE_TRANSFORM, + load_transform: VERTICAL_FLIP_2X2_LOAD_TRANSFORM, + }, + &[&view_2x2], + ); + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [0, 1], [1, 1]], + wgpu::BindingResource::ExternalTexture(&flip_v_external_texture), + ); + assert_eq!(&loads, &[BLUE_F32, YELLOW_F32, RED_F32, GREEN_F32]); + + // Use a non-square texture for the rotation cases as it's more + // interesting + let view_4x1 = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 4, + height: 1, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + + let rotate_90_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 1, + height: 4, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: ROTATE_90_4X1_SAMPLE_TRANSFORM, + load_transform: ROTATE_90_4X1_LOAD_TRANSFORM, + }, + &[&view_4x1], + ); + let loads = get_loads( + &ctx, + &[[0, 0], [0, 1], [0, 2], [0, 3]], + wgpu::BindingResource::ExternalTexture(&rotate_90_external_texture), + ); + assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + + let rotate_180_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 4, + height: 1, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: ROTATE_180_4X1_SAMPLE_TRANSFORM, + load_transform: ROTATE_180_4X1_LOAD_TRANSFORM, + }, + &[&view_4x1], + ); + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [2, 0], [3, 0]], + wgpu::BindingResource::ExternalTexture(&rotate_180_external_texture), + ); + assert_eq!(&loads, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]); + + let rotate_270_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 1, + height: 4, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: ROTATE_270_4X1_SAMPLE_TRANSFORM, + load_transform: ROTATE_270_4X1_LOAD_TRANSFORM, + }, + &[&view_4x1], + ); + let loads = get_loads( + &ctx, + &[[0, 0], [0, 1], [0, 2], [0, 3]], + wgpu::BindingResource::ExternalTexture(&rotate_270_external_texture), + ); + assert_eq!(&loads, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]); + + let view_4x4 = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA_WITH_BORDER.as_flattened()), + ); + let crop_tex = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: CROP_4X4_SAMPLE_TRANSFORM, + load_transform: CROP_4X4_LOAD_TRANSFORM, + }, + &[&view_4x4], + ); + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [0, 1], [1, 1]], + wgpu::BindingResource::ExternalTexture(&crop_tex), + ); + assert_eq!(&loads, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + }); + +/// Tests that `textureLoad()` for an invalid address returns an allowed value. +#[gpu_test] +static EXTERNAL_TEXTURE_LOAD_INVALID_ADDRESS: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + // Create a 2x2 texture, but specify the width and height of the + // external texture to be 1x1. (0, 1), (1, 0), and (1, 1) will + // therefore be invalid addresses, despite falling within the bounds of + // the underlying texture. + let view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + + let external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 1, + height: 1, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: IDENTITY_SAMPLE_TRANSFORM, + load_transform: IDENTITY_LOAD_TRANSFORM, + }, + &[&view], + ); + + let loads = get_loads( + &ctx, + &[[0, 0], [1, 0], [0, 1], [1, 1]], + wgpu::BindingResource::ExternalTexture(&external_texture), + ); + for load in &loads { + // From https://www.w3.org/TR/WGSL/#textureload: + // If the logical texel address is invalid, the built-in function returns one of: + // * The data for some texel within bounds of the texture + // * A vector (0,0,0,0) or (0,0,0,1) of the appropriate type for non-depth textures + // * 0.0 for depth textures + // We therefore expect the loaded values to be red, opaque black, + // or transparent black. They must not be green, blue, or yellow. + assert!([RED_F32, OPAQUE_BLACK_F32, TRANSPARENT_BLACK_F32].contains(load)); + } + }); + +/// Tests that `textureSampleBaseClampToEdge()` returns the correct values for both RGBA +/// format external textures and texture views bound to an external texture binding. +#[gpu_test] +static EXTERNAL_TEXTURE_SAMPLE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + let view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + let samples = get_samples( + &ctx, + &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]], + wgpu::BindingResource::TextureView(&view), + ); + assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + + let external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: IDENTITY_SAMPLE_TRANSFORM, + load_transform: IDENTITY_LOAD_TRANSFORM, + }, + &[&view], + ); + + let samples = get_samples( + &ctx, + &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]], + wgpu::BindingResource::ExternalTexture(&external_texture), + ); + assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + }); + +/// Tests that `textureSampleBaseClampToEdge()` returns the correct values for YUV +/// format external textures. +#[gpu_test] +static EXTERNAL_TEXTURE_SAMPLE_YUV: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + let y_view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::R8Unorm, + Some(&Y_TEXTURE_DATA), + ); + let u_view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::R8Unorm, + Some(&U_TEXTURE_DATA), + ); + let v_view = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::R8Unorm, + Some(&V_TEXTURE_DATA), + ); + + let external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 4, + height: 4, + format: wgpu::ExternalTextureFormat::Yu12, + yuv_conversion_matrix: BT601_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: BT601_TO_SRGB_GAMUT_CONVERSION_MATRIX, + src_transfer_function: SRGB_TRANSFER_FUNCTION, + dst_transfer_function: SRGB_TRANSFER_FUNCTION, + sample_transform: IDENTITY_SAMPLE_TRANSFORM, + load_transform: IDENTITY_LOAD_TRANSFORM, + }, + &[&y_view, &u_view, &v_view], + ); + let samples = get_samples( + &ctx, + &[ + [0.375, 0.375], + [0.625, 0.375], + [0.375, 0.625], + [0.625, 0.625], + ], + wgpu::BindingResource::ExternalTexture(&external_texture), + ); + + // `assert_abs_diff_eq!()` works for slices but not arrays, hence the + // following tomfoolery. + let samples = samples.iter().map(|arr| arr.as_slice()).collect::>(); + let expected = [RED_SRGB_F32, GREEN_SRGB_F32, BLUE_SRGB_F32, YELLOW_SRGB_F32]; + let expected = expected.each_ref().map(|arr| arr.as_slice()); + // We expect slight inaccuracies due to floating point maths. + assert_abs_diff_eq!(samples.as_slice(), expected.as_slice(), epsilon = 0.01); + }); + +/// Tests that `textureSampleBaseClampToEdge()` returns the correct values for external +/// textures with various sample transforms. +#[gpu_test] +static EXTERNAL_TEXTURE_SAMPLE_TRANSFORM: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .test_features_limits() + .features(wgpu::Features::EXTERNAL_TEXTURE), + ) + .run_async(|ctx| async move { + let view_2x2 = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 2, + height: 2, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + + let flip_h_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: HORIZONTAL_FLIP_2X2_SAMPLE_TRANSFORM, + load_transform: HORIZONTAL_FLIP_2X2_LOAD_TRANSFORM, + }, + &[&view_2x2], + ); + let samples = get_samples( + &ctx, + &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]], + wgpu::BindingResource::ExternalTexture(&flip_h_external_texture), + ); + assert_eq!(&samples, &[GREEN_F32, RED_F32, YELLOW_F32, BLUE_F32]); + + let flip_v_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: VERTICAL_FLIP_2X2_SAMPLE_TRANSFORM, + load_transform: VERTICAL_FLIP_2X2_LOAD_TRANSFORM, + }, + &[&view_2x2], + ); + let samples = get_samples( + &ctx, + &[[0.25, 0.25], [0.75, 0.25], [0.25, 0.75], [0.75, 0.75]], + wgpu::BindingResource::ExternalTexture(&flip_v_external_texture), + ); + assert_eq!(&samples, &[BLUE_F32, YELLOW_F32, RED_F32, GREEN_F32]); + + // Use a non-square texture for the rotation cases as it's more + // interesting + let view_4x1 = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 4, + height: 1, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA.as_flattened()), + ); + + let rotate_90_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 1, + height: 4, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: ROTATE_90_4X1_SAMPLE_TRANSFORM, + load_transform: ROTATE_90_4X1_LOAD_TRANSFORM, + }, + &[&view_4x1], + ); + let samples = get_samples( + &ctx, + &[[0.5, 0.125], [0.5, 0.375], [0.5, 0.625], [0.5, 0.875]], + wgpu::BindingResource::ExternalTexture(&rotate_90_external_texture), + ); + assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + + let rotate_180_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 4, + height: 1, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: ROTATE_180_4X1_SAMPLE_TRANSFORM, + load_transform: ROTATE_180_4X1_LOAD_TRANSFORM, + }, + &[&view_4x1], + ); + let samples = get_samples( + &ctx, + &[[0.125, 0.5], [0.375, 0.5], [0.625, 0.5], [0.875, 0.5]], + wgpu::BindingResource::ExternalTexture(&rotate_180_external_texture), + ); + assert_eq!(&samples, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]); + + let rotate_270_external_texture = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 1, + height: 4, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: ROTATE_270_4X1_SAMPLE_TRANSFORM, + load_transform: ROTATE_270_4X1_LOAD_TRANSFORM, + }, + &[&view_4x1], + ); + let samples = get_samples( + &ctx, + &[[0.5, 0.125], [0.5, 0.375], [0.5, 0.625], [0.5, 0.875]], + wgpu::BindingResource::ExternalTexture(&rotate_270_external_texture), + ); + assert_eq!(&samples, &[YELLOW_F32, BLUE_F32, GREEN_F32, RED_F32]); + + let view_4x4 = create_texture_and_view( + &ctx, + wgpu::Extent3d { + width: 4, + height: 4, + depth_or_array_layers: 1, + }, + wgpu::TextureFormat::Rgba8Unorm, + Some(RGBA_TEXTURE_DATA_WITH_BORDER.as_flattened()), + ); + let crop_tex = ctx.device.create_external_texture( + &wgpu::ExternalTextureDescriptor { + label: None, + width: 2, + height: 2, + format: wgpu::ExternalTextureFormat::Rgba, + yuv_conversion_matrix: IDENTITY_YUV_CONVERSION_MATRIX, + gamut_conversion_matrix: IDENTITY_GAMUT_CONVERSION_MATRIX, + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: CROP_4X4_SAMPLE_TRANSFORM, + load_transform: CROP_4X4_LOAD_TRANSFORM, + }, + &[&view_4x4], + ); + // Deliberately sample from the edges of the external texture rather + // than the texel centres. This tests that clamping to a half-texel + // from the edge works as expected even when the external texture is + // cropped from a larger texture. If this weren't working, the black + // border would affect the sampled values. + let samples = get_samples( + &ctx, + &[[0.0, 0.0], [1.0, 0.0], [0.0, 1.0], [1.0, 1.0]], + wgpu::BindingResource::ExternalTexture(&crop_tex), + ); + assert_eq!(&samples, &[RED_F32, GREEN_F32, BLUE_F32, YELLOW_F32]); + }); diff --git a/tests/tests/wgpu-gpu/external_texture/sample.wgsl b/tests/tests/wgpu-gpu/external_texture/sample.wgsl new file mode 100644 index 00000000000..ab6e862da99 --- /dev/null +++ b/tests/tests/wgpu-gpu/external_texture/sample.wgsl @@ -0,0 +1,14 @@ +@group(0) @binding(0) +var tex: texture_external; +@group(0) @binding(1) +var samp: sampler; + +@group(0) @binding(2) +var coords: array>; +@group(0) @binding(3) +var output: array>; + +@compute @workgroup_size(1) +fn main(@builtin(global_invocation_id) id: vec3) { + output[id.x] = textureSampleBaseClampToEdge(tex, samp, coords[id.x]); +} diff --git a/tests/tests/wgpu-gpu/float32_filterable.rs b/tests/tests/wgpu-gpu/float32_filterable.rs index 5376f924d17..c18a3e6416d 100644 --- a/tests/tests/wgpu-gpu/float32_filterable.rs +++ b/tests/tests/wgpu-gpu/float32_filterable.rs @@ -1,6 +1,13 @@ //! Tests for FLOAT32_FILTERABLE feature. -use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + FLOAT32_FILTERABLE_WITHOUT_FEATURE, + FLOAT32_FILTERABLE_WITH_FEATURE, + ]); +} fn create_texture_binding(device: &wgpu::Device, format: wgpu::TextureFormat, filterable: bool) { let texture = device.create_texture(&wgpu::TextureDescriptor { @@ -46,7 +53,7 @@ fn create_texture_binding(device: &wgpu::Device, format: wgpu::TextureFormat, fi #[gpu_test] static FLOAT32_FILTERABLE_WITHOUT_FEATURE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let device = &ctx.device; // Unorm textures are always filterable @@ -73,7 +80,11 @@ static FLOAT32_FILTERABLE_WITHOUT_FEATURE: GpuTestConfiguration = GpuTestConfigu #[gpu_test] static FLOAT32_FILTERABLE_WITH_FEATURE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::FLOAT32_FILTERABLE)) + .parameters( + TestParameters::default() + .features(wgpu::Features::FLOAT32_FILTERABLE) + .enable_noop(), + ) .run_sync(|ctx| { let device = &ctx.device; // With the feature enabled, it does work! diff --git a/tests/tests/wgpu-gpu/image_atomics/mod.rs b/tests/tests/wgpu-gpu/image_atomics/mod.rs index 0063602f4d0..0d1b3d080da 100644 --- a/tests/tests/wgpu-gpu/image_atomics/mod.rs +++ b/tests/tests/wgpu-gpu/image_atomics/mod.rs @@ -2,9 +2,19 @@ use wgpu::ShaderModuleDescriptor; use wgpu_test::{ - fail, gpu_test, image::ReadbackBuffers, GpuTestConfiguration, TestParameters, TestingContext, + fail, gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, + TestParameters, TestingContext, }; +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + IMAGE_64_ATOMICS, + IMAGE_32_ATOMICS, + IMAGE_ATOMICS_NOT_ENABLED, + IMAGE_ATOMICS_NOT_SUPPORTED, + ]); +} + #[gpu_test] static IMAGE_64_ATOMICS: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( @@ -162,7 +172,7 @@ async fn test_format( #[gpu_test] static IMAGE_ATOMICS_NOT_ENABLED: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let size = wgpu::Extent3d { width: 256, @@ -190,7 +200,7 @@ static IMAGE_ATOMICS_NOT_ENABLED: GpuTestConfiguration = GpuTestConfiguration::n #[gpu_test] static IMAGE_ATOMICS_NOT_SUPPORTED: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_ATOMIC)) + .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_ATOMIC).enable_noop()) .run_sync(|ctx| { let size = wgpu::Extent3d { width: 256, diff --git a/tests/tests/wgpu-gpu/instance.rs b/tests/tests/wgpu-gpu/instance.rs index c74b7aedd27..adab85f2a33 100644 --- a/tests/tests/wgpu-gpu/instance.rs +++ b/tests/tests/wgpu-gpu/instance.rs @@ -1,4 +1,10 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(INITIALIZE); +} #[gpu_test] -static INITIALIZE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|_ctx| {}); +static INITIALIZE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|_ctx| {}); diff --git a/tests/tests/wgpu-gpu/life_cycle.rs b/tests/tests/wgpu-gpu/life_cycle.rs index ff0b90b0247..1e5d368c578 100644 --- a/tests/tests/wgpu-gpu/life_cycle.rs +++ b/tests/tests/wgpu-gpu/life_cycle.rs @@ -1,9 +1,19 @@ -use wgpu::{util::DeviceExt, Backends}; -use wgpu_test::{fail, gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +use wgpu::util::DeviceExt; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + BUFFER_DESTROY, + TEXTURE_DESTROY, + BUFFER_DESTROY_BEFORE_SUBMIT, + TEXTURE_DESTROY_BEFORE_SUBMIT, + ]); +} #[gpu_test] -static BUFFER_DESTROY: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static BUFFER_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { label: Some("buffer"), size: 256, @@ -15,7 +25,9 @@ static BUFFER_DESTROY: GpuTestConfiguration = buffer.destroy(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); fail( &ctx.device, @@ -29,7 +41,9 @@ static BUFFER_DESTROY: GpuTestConfiguration = buffer.destroy(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); buffer.destroy(); @@ -51,7 +65,9 @@ static BUFFER_DESTROY: GpuTestConfiguration = } let buffer = ctx.device.create_buffer(&descriptor); buffer.destroy(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let buffer = ctx.device.create_buffer(&descriptor); buffer.destroy(); { @@ -60,17 +76,22 @@ static BUFFER_DESTROY: GpuTestConfiguration = let buffer = ctx.device.create_buffer(&descriptor); buffer.destroy(); let buffer = ctx.device.create_buffer(&descriptor); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); buffer.destroy(); } let buffer = ctx.device.create_buffer(&descriptor); buffer.destroy(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); }); #[gpu_test] -static TEXTURE_DESTROY: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static TEXTURE_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { @@ -90,11 +111,15 @@ static TEXTURE_DESTROY: GpuTestConfiguration = texture.destroy(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); texture.destroy(); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); texture.destroy(); @@ -105,10 +130,7 @@ static TEXTURE_DESTROY: GpuTestConfiguration = // submission fails gracefully. #[gpu_test] static BUFFER_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters( - // https://github.com/gfx-rs/wgpu/issues/7854 - TestParameters::default().skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")), - ) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let buffer_source = ctx .device @@ -145,10 +167,7 @@ static BUFFER_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration // submission fails gracefully. #[gpu_test] static TEXTURE_DESTROY_BEFORE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters( - // https://github.com/gfx-rs/wgpu/issues/7854 - TestParameters::default().skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")), - ) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let descriptor = wgpu::TextureDescriptor { label: None, diff --git a/tests/tests/wgpu-gpu/main.rs b/tests/tests/wgpu-gpu/main.rs index 8fd24c43435..b52e5088ebb 100644 --- a/tests/tests/wgpu-gpu/main.rs +++ b/tests/tests/wgpu-gpu/main.rs @@ -1,14 +1,14 @@ mod regression { - mod issue_3349; - mod issue_3457; - mod issue_4024; - mod issue_4122; - mod issue_4485; - mod issue_4514; - mod issue_5553; - mod issue_6317; - mod issue_6467; - mod issue_6827; + pub mod issue_3349; + pub mod issue_3457; + pub mod issue_4024; + pub mod issue_4122; + pub mod issue_4485; + pub mod issue_4514; + pub mod issue_5553; + pub mod issue_6317; + pub mod issue_6467; + pub mod issue_6827; } mod bgra8unorm_storage; @@ -28,18 +28,20 @@ mod dispatch_workgroups_indirect; mod draw_indirect; mod dual_source_blending; mod encoder; +mod external_image_copy; mod external_texture; mod float32_filterable; mod image_atomics; mod instance; mod life_cycle; mod mem_leaks; -mod nv12_texture; +mod mesh_shader; mod occlusion_query; mod oob_indexing; mod oom; mod pipeline; mod pipeline_cache; +mod planar_texture; mod poll; mod push_constants; mod query_set; @@ -65,7 +67,82 @@ mod transfer; mod transition_resources; mod vertex_formats; mod vertex_indices; +mod vertex_state; mod write_texture; mod zero_init_texture_after_discard; -wgpu_test::gpu_test_main!(); +fn all_tests() -> Vec { + let mut tests = Vec::new(); + + bgra8unorm_storage::all_tests(&mut tests); + bind_group_layout_dedup::all_tests(&mut tests); + bind_groups::all_tests(&mut tests); + binding_array::all_tests(&mut tests); + buffer_copy::all_tests(&mut tests); + buffer_usages::all_tests(&mut tests); + buffer::all_tests(&mut tests); + clear_texture::all_tests(&mut tests); + clip_distances::all_tests(&mut tests); + cloneable_types::all_tests(&mut tests); + compute_pass_ownership::all_tests(&mut tests); + device::all_tests(&mut tests); + dispatch_workgroups_indirect::all_tests(&mut tests); + draw_indirect::all_tests(&mut tests); + dual_source_blending::all_tests(&mut tests); + encoder::all_tests(&mut tests); + external_texture::all_tests(&mut tests); + float32_filterable::all_tests(&mut tests); + image_atomics::all_tests(&mut tests); + instance::all_tests(&mut tests); + life_cycle::all_tests(&mut tests); + mem_leaks::all_tests(&mut tests); + mesh_shader::all_tests(&mut tests); + occlusion_query::all_tests(&mut tests); + oob_indexing::all_tests(&mut tests); + oom::all_tests(&mut tests); + pipeline_cache::all_tests(&mut tests); + pipeline::all_tests(&mut tests); + planar_texture::all_tests(&mut tests); + poll::all_tests(&mut tests); + push_constants::all_tests(&mut tests); + query_set::all_tests(&mut tests); + queue_transfer::all_tests(&mut tests); + ray_tracing::all_tests(&mut tests); + regression::issue_3349::all_tests(&mut tests); + regression::issue_3457::all_tests(&mut tests); + regression::issue_4024::all_tests(&mut tests); + regression::issue_4122::all_tests(&mut tests); + regression::issue_4485::all_tests(&mut tests); + regression::issue_4514::all_tests(&mut tests); + regression::issue_5553::all_tests(&mut tests); + regression::issue_6317::all_tests(&mut tests); + regression::issue_6467::all_tests(&mut tests); + regression::issue_6827::all_tests(&mut tests); + render_pass_ownership::all_tests(&mut tests); + render_target::all_tests(&mut tests); + resource_descriptor_accessor::all_tests(&mut tests); + resource_error::all_tests(&mut tests); + samplers::all_tests(&mut tests); + scissor_tests::all_tests(&mut tests); + shader_primitive_index::all_tests(&mut tests); + shader_view_format::all_tests(&mut tests); + shader::all_tests(&mut tests); + subgroup_operations::all_tests(&mut tests); + texture_binding::all_tests(&mut tests); + texture_blit::all_tests(&mut tests); + texture_bounds::all_tests(&mut tests); + texture_view_creation::all_tests(&mut tests); + timestamp_normalization::all_tests(&mut tests); + timestamp_query::all_tests(&mut tests); + transfer::all_tests(&mut tests); + transition_resources::all_tests(&mut tests); + vertex_formats::all_tests(&mut tests); + vertex_indices::all_tests(&mut tests); + vertex_state::all_tests(&mut tests); + write_texture::all_tests(&mut tests); + zero_init_texture_after_discard::all_tests(&mut tests); + + tests +} + +wgpu_test::gpu_test_main!(all_tests()); diff --git a/tests/tests/wgpu-gpu/mem_leaks.rs b/tests/tests/wgpu-gpu/mem_leaks.rs index 775d3aa5cb4..ada016b1f7e 100644 --- a/tests/tests/wgpu-gpu/mem_leaks.rs +++ b/tests/tests/wgpu-gpu/mem_leaks.rs @@ -1,3 +1,17 @@ +#[allow( + clippy::allow_attributes, + reason = "Using expect is going to be much more verbose" +)] +#[allow(clippy::ptr_arg)] +pub fn all_tests(_vec: &mut Vec) { + #[cfg(any( + not(target_arch = "wasm32"), + target_os = "emscripten", + feature = "webgl" + ))] + _vec.push(SIMPLE_DRAW_CHECK_MEM_LEAKS); +} + #[cfg(any( not(target_arch = "wasm32"), target_os = "emscripten", @@ -179,7 +193,7 @@ async fn draw_test_with_reports( let global_report = ctx.instance.generate_report().unwrap(); let report = global_report.hub_report(); - assert_eq!(report.command_buffers.num_allocated, 1); + assert_eq!(report.command_encoders.num_allocated, 1); assert_eq!(report.buffers.num_allocated, 1); let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { @@ -206,7 +220,7 @@ async fn draw_test_with_reports( assert_eq!(report.pipeline_layouts.num_allocated, 1); assert_eq!(report.render_pipelines.num_allocated, 1); assert_eq!(report.compute_pipelines.num_allocated, 0); - assert_eq!(report.command_buffers.num_allocated, 1); + assert_eq!(report.command_encoders.num_allocated, 1); assert_eq!(report.render_bundles.num_allocated, 0); assert_eq!(report.texture_views.num_allocated, 1); assert_eq!(report.textures.num_allocated, 1); @@ -223,7 +237,7 @@ async fn draw_test_with_reports( let global_report = ctx.instance.generate_report().unwrap(); let report = global_report.hub_report(); - assert_eq!(report.command_buffers.num_kept_from_user, 1); + assert_eq!(report.command_encoders.num_kept_from_user, 1); assert_eq!(report.render_pipelines.num_kept_from_user, 0); assert_eq!(report.pipeline_layouts.num_kept_from_user, 0); assert_eq!(report.bind_group_layouts.num_kept_from_user, 0); @@ -231,7 +245,7 @@ async fn draw_test_with_reports( assert_eq!(report.buffers.num_kept_from_user, 0); assert_eq!(report.texture_views.num_kept_from_user, 0); assert_eq!(report.textures.num_kept_from_user, 0); - assert_eq!(report.command_buffers.num_allocated, 1); + assert_eq!(report.command_encoders.num_allocated, 1); assert_eq!(report.render_pipelines.num_allocated, 0); assert_eq!(report.pipeline_layouts.num_allocated, 0); assert_eq!(report.bind_group_layouts.num_allocated, 0); @@ -240,16 +254,25 @@ async fn draw_test_with_reports( assert_eq!(report.texture_views.num_allocated, 0); assert_eq!(report.textures.num_allocated, 0); - let submit_index = ctx.queue.submit(Some(encoder.finish())); + let command_buffer = encoder.finish(); + + let global_report = ctx.instance.generate_report().unwrap(); + let report = global_report.hub_report(); + assert_eq!(report.command_encoders.num_allocated, 0); + assert_eq!(report.command_buffers.num_allocated, 1); - // TODO: fix in https://github.com/gfx-rs/wgpu/pull/5141 - // let global_report = ctx.instance.generate_report().unwrap(); - // let report = global_report.hub_report(); - // assert_eq!(report.command_buffers.num_allocated, 0); + let submit_index = ctx.queue.submit(Some(command_buffer)); + + let global_report = ctx.instance.generate_report().unwrap(); + let report = global_report.hub_report(); + assert_eq!(report.command_buffers.num_allocated, 0); - ctx.async_poll(wgpu::PollType::wait_for(submit_index)) - .await - .unwrap(); + ctx.async_poll(wgpu::PollType::Wait { + submission_index: Some(submit_index), + timeout: None, + }) + .await + .unwrap(); let global_report = ctx.instance.generate_report().unwrap(); let report = global_report.hub_report(); @@ -290,7 +313,8 @@ static SIMPLE_DRAW_CHECK_MEM_LEAKS: wgpu_test::GpuTestConfiguration = .parameters( wgpu_test::TestParameters::default() .test_features_limits() - .features(wgpu::Features::VERTEX_WRITABLE_STORAGE), + .features(wgpu::Features::VERTEX_WRITABLE_STORAGE) + .enable_noop(), ) .run_async(|ctx| { draw_test_with_reports(ctx, &[0, 1, 2, 3, 4, 5], |cmb| { diff --git a/tests/tests/wgpu-gpu/mesh_shader/basic.frag b/tests/tests/wgpu-gpu/mesh_shader/basic.frag new file mode 100644 index 00000000000..9d2b777326f --- /dev/null +++ b/tests/tests/wgpu-gpu/mesh_shader/basic.frag @@ -0,0 +1,9 @@ +#version 450 +#extension GL_EXT_mesh_shader : require + +in VertexInput { layout(location = 0) vec4 color; } +vertexInput; + +layout(location = 0) out vec4 fragColor; + +void main() { fragColor = vertexInput.color; } \ No newline at end of file diff --git a/tests/tests/wgpu-gpu/mesh_shader/basic.hlsl b/tests/tests/wgpu-gpu/mesh_shader/basic.hlsl new file mode 100644 index 00000000000..89a7276d3c0 --- /dev/null +++ b/tests/tests/wgpu-gpu/mesh_shader/basic.hlsl @@ -0,0 +1,41 @@ +struct OutVertex { + float4 Position : SV_POSITION; + float4 Color: COLOR; +}; +struct InVertex { + float4 Color: COLOR; +}; + + +static const float4 positions[3] = {float4(0., 1.0, 0., 1.0), float4(-1.0, -1.0, 0., 1.0), float4(1.0, -1.0, 0., 1.0)}; +static const float4 colors[3] = {float4(0., 1., 0., 1.), float4(0., 0., 1., 1.), float4(1., 0., 0., 1.)}; + +struct EmptyPayload { + uint _nullField; +}; +groupshared EmptyPayload _emptyPayload; + +[numthreads(4, 1, 1)] +void Task() { + DispatchMesh(1, 1, 1, _emptyPayload); +} + +[outputtopology("triangle")] +[numthreads(1, 1, 1)] +void Mesh(out indices uint3 triangles[1], out vertices OutVertex vertices[3], in payload EmptyPayload _emptyPayload) { + SetMeshOutputCounts(3, 1); + + vertices[0].Position = positions[0]; + vertices[1].Position = positions[1]; + vertices[2].Position = positions[2]; + + vertices[0].Color = colors[0]; + vertices[1].Color = colors[1]; + vertices[2].Color = colors[2]; + + triangles[0] = uint3(0, 1, 2); +} + +float4 Frag(InVertex vertex) : SV_Target { + return vertex.Color; +} diff --git a/tests/tests/wgpu-gpu/mesh_shader/basic.mesh b/tests/tests/wgpu-gpu/mesh_shader/basic.mesh new file mode 100644 index 00000000000..400cafb36f0 --- /dev/null +++ b/tests/tests/wgpu-gpu/mesh_shader/basic.mesh @@ -0,0 +1,25 @@ +#version 450 +#extension GL_EXT_mesh_shader : require + +const vec4[3] positions = {vec4(0., 1.0, 0., 1.0), vec4(-1.0, -1.0, 0., 1.0), + vec4(1.0, -1.0, 0., 1.0)}; +const vec4[3] colors = {vec4(0., 1., 0., 1.), vec4(0., 0., 1., 1.), + vec4(1., 0., 0., 1.)}; + +layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in; + +out VertexOutput { layout(location = 0) vec4 color; } +vertexOutput[]; + +layout(triangles, max_vertices = 3, max_primitives = 1) out; + +void main() { + SetMeshOutputsEXT(3, 1); + gl_MeshVerticesEXT[0].gl_Position = positions[0]; + gl_MeshVerticesEXT[1].gl_Position = positions[1]; + gl_MeshVerticesEXT[2].gl_Position = positions[2]; + vertexOutput[0].color = colors[0]; + vertexOutput[1].color = colors[1]; + vertexOutput[2].color = colors[2]; + gl_PrimitiveTriangleIndicesEXT[gl_LocalInvocationIndex] = uvec3(0, 1, 2); +} \ No newline at end of file diff --git a/tests/tests/wgpu-gpu/mesh_shader/basic.task b/tests/tests/wgpu-gpu/mesh_shader/basic.task new file mode 100644 index 00000000000..418cffa3f6c --- /dev/null +++ b/tests/tests/wgpu-gpu/mesh_shader/basic.task @@ -0,0 +1,6 @@ +#version 450 +#extension GL_EXT_mesh_shader : require + +layout(local_size_x = 4, local_size_y = 1, local_size_z = 1) in; + +void main() { EmitMeshTasksEXT(1, 1, 1); } \ No newline at end of file diff --git a/tests/tests/wgpu-gpu/mesh_shader/mod.rs b/tests/tests/wgpu-gpu/mesh_shader/mod.rs new file mode 100644 index 00000000000..3d7f6df6068 --- /dev/null +++ b/tests/tests/wgpu-gpu/mesh_shader/mod.rs @@ -0,0 +1,449 @@ +use std::{ + hash::{DefaultHasher, Hash, Hasher}, + process::Stdio, +}; + +use wgpu::util::DeviceExt; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + MESH_PIPELINE_BASIC_MESH, + MESH_PIPELINE_BASIC_TASK_MESH, + MESH_PIPELINE_BASIC_MESH_FRAG, + MESH_PIPELINE_BASIC_TASK_MESH_FRAG, + MESH_DRAW_INDIRECT, + MESH_MULTI_DRAW_INDIRECT, + MESH_MULTI_DRAW_INDIRECT_COUNT, + MESH_PIPELINE_BASIC_MESH_NO_DRAW, + MESH_PIPELINE_BASIC_TASK_MESH_FRAG_NO_DRAW, + ]); +} + +// Same as in mesh shader example +fn compile_glsl(device: &wgpu::Device, shader_stage: &'static str) -> wgpu::ShaderModule { + let cmd = std::process::Command::new("glslc") + .args([ + &format!( + "{}/tests/wgpu-gpu/mesh_shader/basic.{shader_stage}", + env!("CARGO_MANIFEST_DIR") + ), + "-o", + "-", + "--target-env=vulkan1.2", + "--target-spv=spv1.4", + ]) + .stdin(Stdio::piped()) + .stdout(Stdio::piped()) + .spawn() + .expect("Failed to call glslc"); + let output = cmd.wait_with_output().expect("Error waiting for glslc"); + assert!(output.status.success()); + unsafe { + device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { + entry_point: "main".into(), + label: None, + spirv: Some(wgpu::util::make_spirv_raw(&output.stdout)), + ..Default::default() + }) + } +} + +fn compile_hlsl( + device: &wgpu::Device, + entry: &str, + stage_str: &str, + test_name: &str, +) -> wgpu::ShaderModule { + // Each test needs its own files + let out_path = format!( + "{}/tests/wgpu-gpu/mesh_shader/{test_name}.{stage_str}.cso", + env!("CARGO_MANIFEST_DIR") + ); + let cmd = std::process::Command::new("dxc") + .args([ + "-T", + &format!("{stage_str}_6_5"), + "-E", + entry, + &format!( + "{}/tests/wgpu-gpu/mesh_shader/basic.hlsl", + env!("CARGO_MANIFEST_DIR") + ), + "-Fo", + &out_path, + ]) + .output() + .unwrap(); + if !cmd.status.success() { + panic!("DXC failed:\n{}", String::from_utf8(cmd.stderr).unwrap()); + } + let file = std::fs::read(&out_path).unwrap(); + std::fs::remove_file(out_path).unwrap(); + unsafe { + device.create_shader_module_passthrough(wgpu::ShaderModuleDescriptorPassthrough { + entry_point: entry.to_owned(), + label: None, + num_workgroups: (1, 1, 1), + dxil: Some(std::borrow::Cow::Owned(file)), + ..Default::default() + }) + } +} + +fn get_shaders( + device: &wgpu::Device, + backend: wgpu::Backend, + test_name: &str, +) -> (wgpu::ShaderModule, wgpu::ShaderModule, wgpu::ShaderModule) { + if backend == wgpu::Backend::Vulkan { + ( + compile_glsl(device, "task"), + compile_glsl(device, "mesh"), + compile_glsl(device, "frag"), + ) + } else if backend == wgpu::Backend::Dx12 { + ( + compile_hlsl(device, "Task", "as", test_name), + compile_hlsl(device, "Mesh", "ms", test_name), + compile_hlsl(device, "Frag", "ps", test_name), + ) + } else { + unreachable!() + } +} + +fn create_depth( + device: &wgpu::Device, +) -> (wgpu::Texture, wgpu::TextureView, wgpu::DepthStencilState) { + let image_size = wgpu::Extent3d { + width: 64, + height: 64, + depth_or_array_layers: 1, + }; + let depth_texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: image_size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Depth32Float, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + let depth_view = depth_texture.create_view(&Default::default()); + let state = wgpu::DepthStencilState { + format: wgpu::TextureFormat::Depth32Float, + depth_write_enabled: true, + depth_compare: wgpu::CompareFunction::Less, // 1. + stencil: wgpu::StencilState::default(), // 2. + bias: wgpu::DepthBiasState::default(), + }; + (depth_texture, depth_view, state) +} + +struct MeshPipelineTestInfo { + use_task: bool, + use_frag: bool, + draw: bool, +} + +fn hash_testing_context(ctx: &TestingContext) -> u64 { + let mut hasher = DefaultHasher::new(); + ctx.hash(&mut hasher); + hasher.finish() +} + +fn mesh_pipeline_build(ctx: &TestingContext, info: MeshPipelineTestInfo) { + let backend = ctx.adapter.get_info().backend; + if backend != wgpu::Backend::Vulkan && backend != wgpu::Backend::Dx12 { + return; + } + let device = &ctx.device; + let (_depth_image, depth_view, depth_state) = create_depth(device); + + let test_hash = hash_testing_context(ctx).to_string(); + let (task, mesh, frag) = get_shaders(device, backend, &test_hash); + let task = if info.use_task { Some(task) } else { None }; + let frag = if info.use_frag { Some(frag) } else { None }; + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor { + label: None, + layout: Some(&layout), + task: task.as_ref().map(|task| wgpu::TaskState { + module: task, + entry_point: Some("main"), + compilation_options: Default::default(), + }), + mesh: wgpu::MeshState { + module: &mesh, + entry_point: Some("main"), + compilation_options: Default::default(), + }, + fragment: frag.as_ref().map(|frag| wgpu::FragmentState { + module: frag, + entry_point: Some("main"), + targets: &[], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + cull_mode: Some(wgpu::Face::Back), + ..Default::default() + }, + depth_stencil: Some(depth_state), + multisample: Default::default(), + multiview: None, + cache: None, + }); + if info.draw { + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: wgpu::StoreOp::Store, + }), + stencil_ops: None, + }), + timestamp_writes: None, + occlusion_query_set: None, + }); + pass.set_pipeline(&pipeline); + pass.draw_mesh_tasks(1, 1, 1); + } + ctx.queue.submit(Some(encoder.finish())); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); + } +} + +#[derive(PartialEq, Eq, Clone, Copy)] +pub enum DrawType { + #[allow(dead_code)] + Standard, + Indirect, + MultiIndirect, + MultiIndirectCount, +} + +fn mesh_draw(ctx: &TestingContext, draw_type: DrawType) { + let backend = ctx.adapter.get_info().backend; + if backend != wgpu::Backend::Vulkan && backend != wgpu::Backend::Dx12 { + return; + } + let device = &ctx.device; + let (_depth_image, depth_view, depth_state) = create_depth(device); + let test_hash = hash_testing_context(ctx).to_string(); + let (task, mesh, frag) = get_shaders(device, backend, &test_hash); + let layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: None, + bind_group_layouts: &[], + push_constant_ranges: &[], + }); + let pipeline = device.create_mesh_pipeline(&wgpu::MeshPipelineDescriptor { + label: None, + layout: Some(&layout), + task: Some(wgpu::TaskState { + module: &task, + entry_point: Some("main"), + compilation_options: Default::default(), + }), + mesh: wgpu::MeshState { + module: &mesh, + entry_point: Some("main"), + compilation_options: Default::default(), + }, + fragment: Some(wgpu::FragmentState { + module: &frag, + entry_point: Some("main"), + targets: &[], + compilation_options: Default::default(), + }), + primitive: wgpu::PrimitiveState { + cull_mode: Some(wgpu::Face::Back), + ..Default::default() + }, + depth_stencil: Some(depth_state), + multisample: Default::default(), + multiview: None, + cache: None, + }); + let buffer = match draw_type { + DrawType::Standard => None, + DrawType::Indirect | DrawType::MultiIndirect | DrawType::MultiIndirectCount => Some( + device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::INDIRECT, + contents: bytemuck::bytes_of(&[1u32; 4]), + }), + ), + }; + let count_buffer = match draw_type { + DrawType::MultiIndirectCount => Some(device.create_buffer_init( + &wgpu::util::BufferInitDescriptor { + label: None, + usage: wgpu::BufferUsages::INDIRECT, + contents: bytemuck::bytes_of(&[1u32; 1]), + }, + )), + _ => None, + }; + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + { + let mut pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[], + depth_stencil_attachment: Some(wgpu::RenderPassDepthStencilAttachment { + view: &depth_view, + depth_ops: Some(wgpu::Operations { + load: wgpu::LoadOp::Clear(1.0), + store: wgpu::StoreOp::Store, + }), + stencil_ops: None, + }), + timestamp_writes: None, + occlusion_query_set: None, + }); + pass.set_pipeline(&pipeline); + match draw_type { + DrawType::Standard => pass.draw_mesh_tasks(1, 1, 1), + DrawType::Indirect => pass.draw_mesh_tasks_indirect(buffer.as_ref().unwrap(), 0), + DrawType::MultiIndirect => { + pass.multi_draw_mesh_tasks_indirect(buffer.as_ref().unwrap(), 0, 1) + } + DrawType::MultiIndirectCount => pass.multi_draw_mesh_tasks_indirect_count( + buffer.as_ref().unwrap(), + 0, + count_buffer.as_ref().unwrap(), + 0, + 1, + ), + } + pass.draw_mesh_tasks_indirect(buffer.as_ref().unwrap(), 0); + } + ctx.queue.submit(Some(encoder.finish())); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); +} + +fn default_gpu_test_config(draw_type: DrawType) -> GpuTestConfiguration { + GpuTestConfiguration::new().parameters( + TestParameters::default() + .test_features_limits() + .features( + wgpu::Features::EXPERIMENTAL_MESH_SHADER + | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS + | match draw_type { + DrawType::Standard | DrawType::Indirect | DrawType::MultiIndirect => { + wgpu::Features::empty() + } + DrawType::MultiIndirectCount => wgpu::Features::MULTI_DRAW_INDIRECT_COUNT, + }, + ) + .limits(wgpu::Limits::default().using_recommended_minimum_mesh_shader_values()), + ) +} + +#[gpu_test] +pub static MESH_PIPELINE_BASIC_MESH: GpuTestConfiguration = + default_gpu_test_config(DrawType::Standard).run_sync(|ctx| { + mesh_pipeline_build( + &ctx, + MeshPipelineTestInfo { + use_task: false, + use_frag: false, + draw: true, + }, + ); + }); +#[gpu_test] +pub static MESH_PIPELINE_BASIC_TASK_MESH: GpuTestConfiguration = + default_gpu_test_config(DrawType::Standard).run_sync(|ctx| { + mesh_pipeline_build( + &ctx, + MeshPipelineTestInfo { + use_task: true, + use_frag: false, + draw: true, + }, + ); + }); +#[gpu_test] +pub static MESH_PIPELINE_BASIC_MESH_FRAG: GpuTestConfiguration = + default_gpu_test_config(DrawType::Standard).run_sync(|ctx| { + mesh_pipeline_build( + &ctx, + MeshPipelineTestInfo { + use_task: false, + use_frag: true, + draw: true, + }, + ); + }); +#[gpu_test] +pub static MESH_PIPELINE_BASIC_TASK_MESH_FRAG: GpuTestConfiguration = + default_gpu_test_config(DrawType::Standard).run_sync(|ctx| { + mesh_pipeline_build( + &ctx, + MeshPipelineTestInfo { + use_task: true, + use_frag: true, + draw: true, + }, + ); + }); +#[gpu_test] +pub static MESH_PIPELINE_BASIC_MESH_NO_DRAW: GpuTestConfiguration = + default_gpu_test_config(DrawType::Standard).run_sync(|ctx| { + mesh_pipeline_build( + &ctx, + MeshPipelineTestInfo { + use_task: false, + use_frag: false, + draw: false, + }, + ); + }); +#[gpu_test] +pub static MESH_PIPELINE_BASIC_TASK_MESH_FRAG_NO_DRAW: GpuTestConfiguration = + default_gpu_test_config(DrawType::Standard).run_sync(|ctx| { + mesh_pipeline_build( + &ctx, + MeshPipelineTestInfo { + use_task: true, + use_frag: true, + draw: false, + }, + ); + }); + +// Mesh draw +#[gpu_test] +pub static MESH_DRAW_INDIRECT: GpuTestConfiguration = default_gpu_test_config(DrawType::Indirect) + .run_sync(|ctx| { + mesh_draw(&ctx, DrawType::Indirect); + }); +#[gpu_test] +pub static MESH_MULTI_DRAW_INDIRECT: GpuTestConfiguration = + default_gpu_test_config(DrawType::MultiIndirect).run_sync(|ctx| { + mesh_draw(&ctx, DrawType::MultiIndirect); + }); +#[gpu_test] +pub static MESH_MULTI_DRAW_INDIRECT_COUNT: GpuTestConfiguration = + default_gpu_test_config(DrawType::MultiIndirectCount).run_sync(|ctx| { + mesh_draw(&ctx, DrawType::MultiIndirectCount); + }); diff --git a/tests/tests/wgpu-gpu/nv12_texture/mod.rs b/tests/tests/wgpu-gpu/nv12_texture/mod.rs deleted file mode 100644 index 33fe17e8896..00000000000 --- a/tests/tests/wgpu-gpu/nv12_texture/mod.rs +++ /dev/null @@ -1,247 +0,0 @@ -//! Tests for nv12 texture creation and sampling. - -use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters}; - -#[gpu_test] -static NV12_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12)) - .run_sync(|ctx| { - let size = wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }; - let target_format = wgpu::TextureFormat::Bgra8UnormSrgb; - - let shader = ctx - .device - .create_shader_module(wgpu::include_wgsl!("nv12_texture.wgsl")); - let pipeline = ctx - .device - .create_render_pipeline(&wgpu::RenderPipelineDescriptor { - label: Some("nv12 pipeline"), - layout: None, - vertex: wgpu::VertexState { - module: &shader, - entry_point: Some("vs_main"), - compilation_options: Default::default(), - buffers: &[], - }, - fragment: Some(wgpu::FragmentState { - module: &shader, - entry_point: Some("fs_main"), - compilation_options: Default::default(), - targets: &[Some(target_format.into())], - }), - primitive: wgpu::PrimitiveState { - topology: wgpu::PrimitiveTopology::TriangleStrip, - strip_index_format: Some(wgpu::IndexFormat::Uint32), - ..Default::default() - }, - depth_stencil: None, - multisample: wgpu::MultisampleState::default(), - multiview: None, - cache: None, - }); - - let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size, - format: wgpu::TextureFormat::NV12, - usage: wgpu::TextureUsages::TEXTURE_BINDING, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - let y_view = tex.create_view(&wgpu::TextureViewDescriptor { - format: Some(wgpu::TextureFormat::R8Unorm), - aspect: wgpu::TextureAspect::Plane0, - ..Default::default() - }); - let uv_view = tex.create_view(&wgpu::TextureViewDescriptor { - format: Some(wgpu::TextureFormat::Rg8Unorm), - aspect: wgpu::TextureAspect::Plane1, - ..Default::default() - }); - let sampler = ctx.device.create_sampler(&wgpu::SamplerDescriptor { - min_filter: wgpu::FilterMode::Linear, - mag_filter: wgpu::FilterMode::Linear, - ..Default::default() - }); - let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { - label: None, - layout: &pipeline.get_bind_group_layout(0), - entries: &[ - wgpu::BindGroupEntry { - binding: 0, - resource: wgpu::BindingResource::Sampler(&sampler), - }, - wgpu::BindGroupEntry { - binding: 1, - resource: wgpu::BindingResource::TextureView(&y_view), - }, - wgpu::BindGroupEntry { - binding: 2, - resource: wgpu::BindingResource::TextureView(&uv_view), - }, - ], - }); - - let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - size, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: target_format, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT, - view_formats: &[], - }); - let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default()); - - let mut encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { - label: None, - color_attachments: &[Some(wgpu::RenderPassColorAttachment { - ops: wgpu::Operations::default(), - resolve_target: None, - view: &target_view, - depth_slice: None, - })], - depth_stencil_attachment: None, - timestamp_writes: None, - occlusion_query_set: None, - }); - rpass.set_pipeline(&pipeline); - rpass.set_bind_group(0, &bind_group, &[]); - rpass.draw(0..4, 0..1); - drop(rpass); - ctx.queue.submit(Some(encoder.finish())); - }); - -#[gpu_test] -static NV12_TEXTURE_VIEW_PLANE_ON_NON_PLANAR_FORMAT: GpuTestConfiguration = - GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12)) - .run_sync(|ctx| { - let size = wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }; - let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size, - format: wgpu::TextureFormat::R8Unorm, - usage: wgpu::TextureUsages::TEXTURE_BINDING, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - fail( - &ctx.device, - || { - let _ = tex.create_view(&wgpu::TextureViewDescriptor { - aspect: wgpu::TextureAspect::Plane0, - ..Default::default() - }); - }, - Some("aspect plane0 is not in the source texture format r8unorm"), - ); - }); - -#[gpu_test] -static NV12_TEXTURE_VIEW_PLANE_OUT_OF_BOUNDS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12)) - .run_sync(|ctx| { - let size = wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }; - let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size, - format: wgpu::TextureFormat::NV12, - usage: wgpu::TextureUsages::TEXTURE_BINDING, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - fail( - &ctx.device, - || { - let _ = tex.create_view(&wgpu::TextureViewDescriptor { - format: Some(wgpu::TextureFormat::R8Unorm), - aspect: wgpu::TextureAspect::Plane2, - ..Default::default() - }); - }, - Some("aspect plane2 is not in the source texture format nv12"), - ); - }); - -#[gpu_test] -static NV12_TEXTURE_BAD_FORMAT_VIEW_PLANE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12)) - .run_sync(|ctx| { - let size = wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }; - let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size, - format: wgpu::TextureFormat::NV12, - usage: wgpu::TextureUsages::TEXTURE_BINDING, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - fail( - &ctx.device, - || { - let _ = tex.create_view(&wgpu::TextureViewDescriptor { - format: Some(wgpu::TextureFormat::Rg8Unorm), - aspect: wgpu::TextureAspect::Plane0, - ..Default::default() - }); - }, - Some("unable to view texture nv12 as rg8unorm"), - ); - }); - -#[gpu_test] -static NV12_TEXTURE_BAD_SIZE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().features(wgpu::Features::TEXTURE_FORMAT_NV12)) - .run_sync(|ctx| { - let size = wgpu::Extent3d { - width: 255, - height: 255, - depth_or_array_layers: 1, - }; - - fail( - &ctx.device, - || { - let _ = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size, - format: wgpu::TextureFormat::NV12, - usage: wgpu::TextureUsages::TEXTURE_BINDING, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - }, - Some("width 255 is not a multiple of nv12's width multiple requirement"), - ); - }); diff --git a/tests/tests/wgpu-gpu/occlusion_query/mod.rs b/tests/tests/wgpu-gpu/occlusion_query/mod.rs index cb47ac6981c..caa4f7e9d76 100644 --- a/tests/tests/wgpu-gpu/occlusion_query/mod.rs +++ b/tests/tests/wgpu-gpu/occlusion_query/mod.rs @@ -1,5 +1,9 @@ use wgpu::InstanceFlags; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(OCCLUSION_QUERY); +} #[gpu_test] static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new() @@ -121,7 +125,9 @@ static OCCLUSION_QUERY: GpuTestConfiguration = GpuTestConfiguration::new() mapping_buffer .slice(..) .map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let query_buffer_view = mapping_buffer.slice(..).get_mapped_range(); let query_data: &[u64; 3] = bytemuck::from_bytes(&query_buffer_view); diff --git a/tests/tests/wgpu-gpu/oob_indexing.rs b/tests/tests/wgpu-gpu/oob_indexing.rs index be5257b7887..44901df6485 100644 --- a/tests/tests/wgpu-gpu/oob_indexing.rs +++ b/tests/tests/wgpu-gpu/oob_indexing.rs @@ -1,6 +1,13 @@ use wgpu::{Backend, Backends}; use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + RESTRICT_WORKGROUP_PRIVATE_FUNCTION_LET, + D3D12_RESTRICT_DYNAMIC_BUFFERS, + ]); +} + /// Tests that writing and reading to the max length of a container (vec, mat, array) /// in the workgroup, private and function address spaces + let declarations /// will instead write to and read from the last element. @@ -41,7 +48,9 @@ static RESTRICT_WORKGROUP_PRIVATE_FUNCTION_LET: GpuTestConfiguration = GpuTestCo .slice(..) .map_async(wgpu::MapMode::Read, |_| {}); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let view = test_resources.readback_buffer.slice(..).get_mapped_range(); @@ -442,7 +451,9 @@ async fn d3d12_restrict_dynamic_buffers(ctx: TestingContext) { .slice(..) .map_async(wgpu::MapMode::Read, |_| {}); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let view = readback_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/oom.rs b/tests/tests/wgpu-gpu/oom.rs index f2378b22599..c69915b579c 100644 --- a/tests/tests/wgpu-gpu/oom.rs +++ b/tests/tests/wgpu-gpu/oom.rs @@ -5,8 +5,20 @@ use wgpu::{ CreateTlasDescriptor, Error, ErrorFilter, Extent3d, Features, QuerySetDescriptor, QueryType, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, VertexFormat, }; +use wgpu_test::GpuTestInitializer; use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + TEXTURE_OOM_TEST, + BUFFER_OOM_TEST, + MAPPING_BUFFER_OOM_TEST, + QUERY_SET_OOM_TEST, + BLAS_OOM_TEST, + TLAS_OOM_TEST, + ]); +} + // Tests in this file must all end with "OOM_TEST" so that nextest doesn't run any other tests while it runs one of the OOM tests. // This is done so that other tests that create resources will not fail with OOM errors due to the OOM tests running in parallel. @@ -159,10 +171,8 @@ static QUERY_SET_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new() static BLAS_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() - .features(Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) + .features(Features::EXPERIMENTAL_RAY_QUERY) .skip(FailureCase::backend(!OOM_DETECTION_IMPL)) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(Backends::VULKAN, "AMD")) // see comment at the top of the file .skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")), ) @@ -203,10 +213,8 @@ static BLAS_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new() static TLAS_OOM_TEST: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() - .features(Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) + .features(Features::EXPERIMENTAL_RAY_QUERY) .skip(FailureCase::backend(!OOM_DETECTION_IMPL)) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(Backends::VULKAN, "AMD")) // see comment at the top of the file .skip(FailureCase::backend_adapter(Backends::VULKAN, "llvmpipe")), ) diff --git a/tests/tests/wgpu-gpu/pipeline.rs b/tests/tests/wgpu-gpu/pipeline.rs index 8e9c91e5273..aa62032a5f1 100644 --- a/tests/tests/wgpu-gpu/pipeline.rs +++ b/tests/tests/wgpu-gpu/pipeline.rs @@ -1,4 +1,14 @@ -use wgpu_test::{fail, gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE, + COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX, + RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE, + RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX, + NO_TARGETLESS_RENDER, + ]); +} const INVALID_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderModuleDescriptor { label: Some("invalid shader"), @@ -32,7 +42,7 @@ const TRIVIAL_FRAGMENT_SHADER_DESC: wgpu::ShaderModuleDescriptor = wgpu::ShaderM #[gpu_test] static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -62,7 +72,11 @@ static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = #[gpu_test] static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(|ctx| { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -91,7 +105,7 @@ static COMPUTE_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration = #[gpu_test] static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -128,7 +142,11 @@ static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_MODULE: GpuTestConfiguration = #[gpu_test] static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_sync(|ctx| { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); @@ -176,7 +194,7 @@ static RENDER_PIPELINE_DEFAULT_LAYOUT_BAD_BGL_INDEX: GpuTestConfiguration = #[gpu_test] static NO_TARGETLESS_RENDER: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { fail( &ctx.device, diff --git a/tests/tests/wgpu-gpu/pipeline_cache.rs b/tests/tests/wgpu-gpu/pipeline_cache.rs index 0149c336203..04af5b9a628 100644 --- a/tests/tests/wgpu-gpu/pipeline_cache.rs +++ b/tests/tests/wgpu-gpu/pipeline_cache.rs @@ -2,6 +2,10 @@ use std::{fmt::Write, num::NonZeroU64}; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.push(PIPELINE_CACHE); +} + /// We want to test that using a pipeline cache doesn't cause failure /// /// It would be nice if we could also assert that reusing a pipeline cache would make compilation @@ -175,7 +179,9 @@ async fn validate_pipeline( encoder.copy_buffer_to_buffer(gpu_buffer, 0, cpu_buffer, 0, ARRAY_SIZE * 4); ctx.queue.submit([encoder.finish()]); cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = cpu_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/planar_texture/mod.rs b/tests/tests/wgpu-gpu/planar_texture/mod.rs new file mode 100644 index 00000000000..961d4b43f64 --- /dev/null +++ b/tests/tests/wgpu-gpu/planar_texture/mod.rs @@ -0,0 +1,189 @@ +//! Tests for nv12 texture creation and sampling. + +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + NV12_TEXTURE_CREATION_SAMPLING, + P010_TEXTURE_CREATION_SAMPLING, + ]); +} + +// Helper function to test planar texture creation and sampling. +fn test_planar_texture_creation_sampling( + ctx: &TestingContext, + y_view: &wgpu::TextureView, + uv_view: &wgpu::TextureView, +) { + let target_format = wgpu::TextureFormat::Bgra8UnormSrgb; + + let shader = ctx + .device + .create_shader_module(wgpu::include_wgsl!("planar_texture.wgsl")); + let pipeline = ctx + .device + .create_render_pipeline(&wgpu::RenderPipelineDescriptor { + label: Some("planar texture pipeline"), + layout: None, + vertex: wgpu::VertexState { + module: &shader, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + buffers: &[], + }, + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + compilation_options: Default::default(), + targets: &[Some(target_format.into())], + }), + primitive: wgpu::PrimitiveState { + topology: wgpu::PrimitiveTopology::TriangleStrip, + strip_index_format: Some(wgpu::IndexFormat::Uint32), + ..Default::default() + }, + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + multiview: None, + cache: None, + }); + + let sampler = ctx.device.create_sampler(&wgpu::SamplerDescriptor { + min_filter: wgpu::FilterMode::Linear, + mag_filter: wgpu::FilterMode::Linear, + ..Default::default() + }); + let bind_group = ctx.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::Sampler(&sampler), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: wgpu::BindingResource::TextureView(y_view), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: wgpu::BindingResource::TextureView(uv_view), + }, + ], + }); + + let target_tex = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: y_view.texture().size(), + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: target_format, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }); + let target_view = target_tex.create_view(&wgpu::TextureViewDescriptor::default()); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + ops: wgpu::Operations::default(), + resolve_target: None, + view: &target_view, + depth_slice: None, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + rpass.set_pipeline(&pipeline); + rpass.set_bind_group(0, &bind_group, &[]); + rpass.draw(0..4, 0..1); + drop(rpass); + ctx.queue.submit(Some(encoder.finish())); +} + +/// Ensures that creation and sampling of an NV12 format texture works as +/// expected. +#[gpu_test] +static NV12_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .features(wgpu::Features::TEXTURE_FORMAT_NV12) + .enable_noop(), + ) + .run_sync(|ctx| { + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: wgpu::TextureFormat::NV12, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + let y_view = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::R8Unorm), + aspect: wgpu::TextureAspect::Plane0, + ..Default::default() + }); + let uv_view = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::Rg8Unorm), + aspect: wgpu::TextureAspect::Plane1, + ..Default::default() + }); + + test_planar_texture_creation_sampling(&ctx, &y_view, &uv_view); + }); + +/// Ensures that creation and sampling of a P010 format texture works as +/// expected. +#[gpu_test] +static P010_TEXTURE_CREATION_SAMPLING: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters( + TestParameters::default() + .features( + wgpu::Features::TEXTURE_FORMAT_P010 | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM, + ) + .enable_noop(), + ) + .run_sync(|ctx| { + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: wgpu::TextureFormat::P010, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + let y_view = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::R16Unorm), + aspect: wgpu::TextureAspect::Plane0, + ..Default::default() + }); + let uv_view = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(wgpu::TextureFormat::Rg16Unorm), + aspect: wgpu::TextureAspect::Plane1, + ..Default::default() + }); + + test_planar_texture_creation_sampling(&ctx, &y_view, &uv_view); + }); diff --git a/tests/tests/wgpu-gpu/nv12_texture/nv12_texture.wgsl b/tests/tests/wgpu-gpu/planar_texture/planar_texture.wgsl similarity index 100% rename from tests/tests/wgpu-gpu/nv12_texture/nv12_texture.wgsl rename to tests/tests/wgpu-gpu/planar_texture/planar_texture.wgsl diff --git a/tests/tests/wgpu-gpu/poll.rs b/tests/tests/wgpu-gpu/poll.rs index cb93efb3034..ca9fd91adb3 100644 --- a/tests/tests/wgpu-gpu/poll.rs +++ b/tests/tests/wgpu-gpu/poll.rs @@ -1,4 +1,4 @@ -use std::num::NonZeroU64; +use std::{num::NonZeroU64, time::Duration}; use wgpu::{ BindGroupDescriptor, BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, @@ -6,7 +6,24 @@ use wgpu::{ CommandEncoderDescriptor, ComputePassDescriptor, PollType, ShaderStages, }; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + WAIT, + WAIT_WITH_TIMEOUT, + WAIT_WITH_TIMEOUT_MAX, + DOUBLE_WAIT, + WAIT_ON_SUBMISSION, + WAIT_ON_SUBMISSION_WITH_TIMEOUT, + WAIT_ON_SUBMISSION_WITH_TIMEOUT_MAX, + DOUBLE_WAIT_ON_SUBMISSION, + WAIT_OUT_OF_ORDER, + WAIT_AFTER_BAD_SUBMISSION, + ]); +} fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer { let buffer = ctx.device.create_buffer(&BufferDescriptor { @@ -53,54 +70,158 @@ fn generate_dummy_work(ctx: &TestingContext) -> CommandBuffer { } #[gpu_test] -static WAIT: GpuTestConfiguration = GpuTestConfiguration::new().run_async(|ctx| async move { - let cmd_buf = generate_dummy_work(&ctx); +static WAIT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { + let cmd_buf = generate_dummy_work(&ctx); + + ctx.queue.submit(Some(cmd_buf)); + ctx.async_poll(PollType::Wait { + submission_index: None, + timeout: None, + }) + .await + .unwrap(); + }); + +#[gpu_test] +static WAIT_WITH_TIMEOUT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { + let cmd_buf = generate_dummy_work(&ctx); + + ctx.queue.submit(Some(cmd_buf)); + ctx.async_poll(PollType::Wait { + submission_index: None, + timeout: Some(Duration::from_secs(1)), + }) + .await + .unwrap(); + }); + +#[gpu_test] +static WAIT_WITH_TIMEOUT_MAX: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { + let cmd_buf = generate_dummy_work(&ctx); - ctx.queue.submit(Some(cmd_buf)); - ctx.async_poll(PollType::wait()).await.unwrap(); -}); + ctx.queue.submit(Some(cmd_buf)); + ctx.async_poll(PollType::Wait { + submission_index: None, + timeout: Some(Duration::MAX), + }) + .await + .unwrap(); + }); #[gpu_test] -static DOUBLE_WAIT: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static DOUBLE_WAIT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let cmd_buf = generate_dummy_work(&ctx); ctx.queue.submit(Some(cmd_buf)); - ctx.async_poll(PollType::wait()).await.unwrap(); - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: None, + timeout: None, + }) + .await + .unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: None, + timeout: None, + }) + .await + .unwrap(); + }); + +#[gpu_test] +static WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { + let cmd_buf = generate_dummy_work(&ctx); + + let index = ctx.queue.submit(Some(cmd_buf)); + ctx.async_poll(PollType::Wait { + submission_index: Some(index), + timeout: None, + }) + .await + .unwrap(); + }); + +#[gpu_test] +static WAIT_ON_SUBMISSION_WITH_TIMEOUT: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { + let cmd_buf = generate_dummy_work(&ctx); + + let index = ctx.queue.submit(Some(cmd_buf)); + ctx.async_poll(PollType::Wait { + submission_index: Some(index), + timeout: Some(Duration::from_secs(1)), + }) + .await + .unwrap(); }); #[gpu_test] -static WAIT_ON_SUBMISSION: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static WAIT_ON_SUBMISSION_WITH_TIMEOUT_MAX: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let cmd_buf = generate_dummy_work(&ctx); let index = ctx.queue.submit(Some(cmd_buf)); - ctx.async_poll(PollType::wait_for(index)).await.unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: Some(index), + timeout: Some(Duration::MAX), + }) + .await + .unwrap(); }); #[gpu_test] -static DOUBLE_WAIT_ON_SUBMISSION: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static DOUBLE_WAIT_ON_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let cmd_buf = generate_dummy_work(&ctx); let index = ctx.queue.submit(Some(cmd_buf)); - ctx.async_poll(PollType::wait_for(index.clone())) - .await - .unwrap(); - ctx.async_poll(PollType::wait_for(index)).await.unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: Some(index.clone()), + timeout: None, + }) + .await + .unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: Some(index), + timeout: None, + }) + .await + .unwrap(); }); #[gpu_test] -static WAIT_OUT_OF_ORDER: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static WAIT_OUT_OF_ORDER: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let cmd_buf1 = generate_dummy_work(&ctx); let cmd_buf2 = generate_dummy_work(&ctx); let index1 = ctx.queue.submit(Some(cmd_buf1)); let index2 = ctx.queue.submit(Some(cmd_buf2)); - ctx.async_poll(PollType::wait_for(index2)).await.unwrap(); - ctx.async_poll(PollType::wait_for(index1)).await.unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: Some(index2), + timeout: None, + }) + .await + .unwrap(); + ctx.async_poll(PollType::Wait { + submission_index: Some(index1), + timeout: None, + }) + .await + .unwrap(); }); /// Submit a command buffer to the wrong device. A wait poll shouldn't hang. @@ -109,7 +230,11 @@ static WAIT_OUT_OF_ORDER: GpuTestConfiguration = /// console. #[gpu_test] static WAIT_AFTER_BAD_SUBMISSION: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(wgpu_test::TestParameters::default().skip(wgpu_test::FailureCase::webgl2())) + .parameters( + wgpu_test::TestParameters::default() + .skip(wgpu_test::FailureCase::webgl2()) + .enable_noop(), + ) .run_async(wait_after_bad_submission); async fn wait_after_bad_submission(ctx: TestingContext) { @@ -134,5 +259,5 @@ async fn wait_after_bad_submission(ctx: TestingContext) { // Specifically, the failed submission should not cause a new fence value to // be allocated that will not be signalled until further work is // successfully submitted, causing a greater fence value to be signalled. - device2.poll(wgpu::PollType::Wait).unwrap(); + device2.poll(wgpu::PollType::wait_indefinitely()).unwrap(); } diff --git a/tests/tests/wgpu-gpu/push_constants.rs b/tests/tests/wgpu-gpu/push_constants.rs index b3119442f73..569fb426dfa 100644 --- a/tests/tests/wgpu-gpu/push_constants.rs +++ b/tests/tests/wgpu-gpu/push_constants.rs @@ -3,7 +3,13 @@ use std::num::NonZeroU64; use wgpu::util::RenderEncoder; use wgpu::*; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([PARTIAL_UPDATE, RENDER_PASS_TEST]); +} /// We want to test that partial updates to push constants work as expected. /// @@ -144,7 +150,9 @@ async fn partial_update_test(ctx: TestingContext) { encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, 32); ctx.queue.submit([encoder.finish()]); cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = cpu_buffer.slice(..).get_mapped_range(); @@ -362,7 +370,9 @@ async fn render_pass_test(ctx: &TestingContext, use_render_bundle: bool) { let command_buffer = command_encoder.finish(); ctx.queue.submit([command_buffer]); cpu_buffer.slice(..).map_async(MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let mapped_data = cpu_buffer.slice(..).get_mapped_range(); let result = bytemuck::cast_slice::(&mapped_data).to_vec(); drop(mapped_data); diff --git a/tests/tests/wgpu-gpu/query_set.rs b/tests/tests/wgpu-gpu/query_set.rs index 69d0f868dbf..4aee905d3fb 100644 --- a/tests/tests/wgpu-gpu/query_set.rs +++ b/tests/tests/wgpu-gpu/query_set.rs @@ -1,8 +1,12 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(DROP_FAILED_TIMESTAMP_QUERY_SET); +} #[gpu_test] static DROP_FAILED_TIMESTAMP_QUERY_SET: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { // Enter an error scope, so the validation catch-all doesn't // report the error too early. diff --git a/tests/tests/wgpu-gpu/queue_transfer.rs b/tests/tests/wgpu-gpu/queue_transfer.rs index 9196f7fcbf5..ffff6f2d957 100644 --- a/tests/tests/wgpu-gpu/queue_transfer.rs +++ b/tests/tests/wgpu-gpu/queue_transfer.rs @@ -1,10 +1,19 @@ //! Tests for buffer copy validation. use wgpu::PollType; -use wgpu_test::{fail, gpu_test, GpuTestConfiguration}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + QUEUE_WRITE_TEXTURE_THEN_DESTROY, + QUEUE_WRITE_TEXTURE_OVERFLOW, + QUEUE_WRITE_TEXTURE_BUFFER_OOB, + ]); +} #[gpu_test] static QUEUE_WRITE_TEXTURE_THEN_DESTROY: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -49,12 +58,13 @@ static QUEUE_WRITE_TEXTURE_THEN_DESTROY: GpuTestConfiguration = GpuTestConfigura texture.destroy(); ctx.queue.submit([]); - ctx.device.poll(PollType::wait()).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); }); #[gpu_test] -static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = - GpuTestConfiguration::new().run_sync(|ctx| { +static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, size: wgpu::Extent3d { @@ -99,3 +109,54 @@ static QUEUE_WRITE_TEXTURE_OVERFLOW: GpuTestConfiguration = Some("end up overrunning the bounds of the destination texture"), ); }); + +#[gpu_test] +static QUEUE_WRITE_TEXTURE_BUFFER_OOB: GpuTestConfiguration = + GpuTestConfiguration::new().run_sync(|ctx| { + // Test that transfers overrunning the end of the source buffer, or + // where offset + size overflows a u64, are rejected. + for offset in [120, u64::MAX - 3] { + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 146, + height: 25, + depth_or_array_layers: 192, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba32Float, + usage: wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + let data = vec![255; 128]; + + fail( + &ctx.device, + || { + ctx.queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d { x: 0, y: 0, z: 1 }, + aspect: wgpu::TextureAspect::All, + }, + &data, + wgpu::TexelCopyBufferLayout { + offset, + bytes_per_row: Some(16), + rows_per_image: Some(1), + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + ); + }, + Some("would end up overrunning the bounds of the source buffer"), + ); + } + }); diff --git a/tests/tests/wgpu-gpu/ray_tracing/as_build.rs b/tests/tests/wgpu-gpu/ray_tracing/as_build.rs index 3ef6e7be28a..fe42272e9d5 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/as_build.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/as_build.rs @@ -4,18 +4,36 @@ use crate::ray_tracing::{acceleration_structure_limits, AsBuildContext}; use wgpu::util::{BufferInitDescriptor, DeviceExt}; use wgpu::*; use wgpu_test::{ - fail, fail_if, gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext, + fail, fail_if, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, + TestingContext, }; +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + UNBUILT_BLAS, + UNBUILT_BLAS_COMPACTION, + BLAS_COMPACTION_WITHOUT_FLAGS, + UNPREPARED_BLAS_COMPACTION, + BLAS_COMPACTION, + OUT_OF_ORDER_AS_BUILD, + OUT_OF_ORDER_AS_BUILD_USE, + EMPTY_BUILD, + BUILD_WITH_TRANSFORM, + ONLY_BLAS_VERTEX_RETURN, + ONLY_TLAS_VERTEX_RETURN, + EXTRA_FORMAT_BUILD, + MISALIGNED_BUILD, + TOO_SMALL_STRIDE_BUILD, + ]); +} + #[gpu_test] static UNBUILT_BLAS: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY), ) .run_sync(unbuilt_blas); @@ -48,9 +66,8 @@ static UNBUILT_BLAS_COMPACTION: GpuTestConfiguration = GpuTestConfiguration::new TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(unbuilt_blas_compaction); @@ -77,9 +94,7 @@ static BLAS_COMPACTION_WITHOUT_FLAGS: GpuTestConfiguration = GpuTestConfiguratio TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY), ) .run_sync(blas_compaction_without_flags); @@ -114,9 +129,8 @@ static UNPREPARED_BLAS_COMPACTION: GpuTestConfiguration = GpuTestConfiguration:: TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(unprepared_blas_compaction); @@ -144,9 +158,7 @@ static BLAS_COMPACTION: GpuTestConfiguration = GpuTestConfiguration::new() TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY), ) .run_sync(blas_compaction); @@ -174,7 +186,7 @@ fn blas_compaction(ctx: TestingContext) { }); // On native this will trigger the callback. - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); // Check that the callback actually gets called (this test will timeout if it doesn't). recv.recv().unwrap(); // This should return true because the callback has been called, and we haven't rebuilt the BLAS @@ -203,9 +215,7 @@ static OUT_OF_ORDER_AS_BUILD: GpuTestConfiguration = GpuTestConfiguration::new() TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY), ) .run_sync(out_of_order_as_build); @@ -287,12 +297,8 @@ static OUT_OF_ORDER_AS_BUILD_USE: GpuTestConfiguration = GpuTestConfiguration::n TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features( - wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::EXPERIMENTAL_RAY_QUERY, - ) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(out_of_order_as_build_use); @@ -474,7 +480,8 @@ static EMPTY_BUILD: GpuTestConfiguration = GpuTestConfiguration::new() TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(empty_build); fn empty_build(ctx: TestingContext) { @@ -495,9 +502,8 @@ static BUILD_WITH_TRANSFORM: GpuTestConfiguration = GpuTestConfiguration::new() TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(build_with_transform); @@ -583,12 +589,9 @@ static ONLY_BLAS_VERTEX_RETURN: GpuTestConfiguration = GpuTestConfiguration::new .test_features_limits() .limits(acceleration_structure_limits()) .features( - wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::EXPERIMENTAL_RAY_QUERY + wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN, - ) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + ), ) .run_sync(only_blas_vertex_return); @@ -710,12 +713,10 @@ static ONLY_TLAS_VERTEX_RETURN: GpuTestConfiguration = GpuTestConfiguration::new .test_features_limits() .limits(acceleration_structure_limits()) .features( - wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::EXPERIMENTAL_RAY_QUERY + wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN, ) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .enable_noop(), ) .run_sync(only_tlas_vertex_return); @@ -752,11 +753,10 @@ static EXTRA_FORMAT_BUILD: GpuTestConfiguration = GpuTestConfiguration::new() .test_features_limits() .limits(acceleration_structure_limits()) .features( - wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE + wgpu::Features::EXPERIMENTAL_RAY_QUERY | wgpu::Features::EXTENDED_ACCELERATION_STRUCTURE_VERTEX_FORMATS, ) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .enable_noop(), ) .run_sync(|ctx| test_as_build_format_stride(ctx, VertexFormat::Snorm16x4, 6, false)); @@ -766,9 +766,8 @@ static MISALIGNED_BUILD: GpuTestConfiguration = GpuTestConfiguration::new() TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) // Larger than the minimum size, but not aligned as required .run_sync(|ctx| test_as_build_format_stride(ctx, VertexFormat::Float32x3, 13, true)); @@ -779,9 +778,8 @@ static TOO_SMALL_STRIDE_BUILD: GpuTestConfiguration = GpuTestConfiguration::new( TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) // Aligned as required, but smaller than minimum size .run_sync(|ctx| test_as_build_format_stride(ctx, VertexFormat::Float32x3, 8, true)); diff --git a/tests/tests/wgpu-gpu/ray_tracing/as_create.rs b/tests/tests/wgpu-gpu/ray_tracing/as_create.rs index f6f2e57c352..8f424c5f1bf 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/as_create.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/as_create.rs @@ -8,13 +8,22 @@ use wgpu::{IndexFormat, VertexFormat}; use wgpu_macros::gpu_test; use wgpu_test::{fail, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + BLAS_INVALID_VERTEX_FORMAT, + BLAS_MISMATCHED_INDEX, + UNSUPPORTED_ACCELERATION_STRUCTURE_RESOURCES, + ]); +} + #[gpu_test] static BLAS_INVALID_VERTEX_FORMAT: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(invalid_vertex_format_blas_create); @@ -55,7 +64,8 @@ static BLAS_MISMATCHED_INDEX: GpuTestConfiguration = GpuTestConfiguration::new() TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(mismatched_index_blas_create); @@ -118,3 +128,42 @@ fn mismatched_index_blas_create(ctx: TestingContext) { None, ); } + +#[gpu_test] +static UNSUPPORTED_ACCELERATION_STRUCTURE_RESOURCES: GpuTestConfiguration = + GpuTestConfiguration::new() + .parameters(TestParameters::default().test_features_limits()) + .run_sync(unsupported_acceleration_structure_resources); + +fn unsupported_acceleration_structure_resources(ctx: TestingContext) { + fail( + &ctx.device, + || { + ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 4, + usage: wgpu::BufferUsages::BLAS_INPUT, + mapped_at_creation: false, + }) + }, + None, + ); + fail( + &ctx.device, + || { + ctx.device + .create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: None, + entries: &[wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::AccelerationStructure { + vertex_return: false, + }, + count: None, + }], + }) + }, + None, + ); +} diff --git a/tests/tests/wgpu-gpu/ray_tracing/as_use_after_free.rs b/tests/tests/wgpu-gpu/ray_tracing/as_use_after_free.rs index 3e9c5a45e23..e5213b3e36e 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/as_use_after_free.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/as_use_after_free.rs @@ -11,11 +11,14 @@ use wgpu::{ PollType, TlasInstance, VertexFormat, }; use wgpu_macros::gpu_test; -use wgpu_test::{FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{GpuTestConfiguration, TestParameters, TestingContext}; + +pub fn all_tests(tests: &mut Vec) { + tests.push(ACCELERATION_STRUCTURE_USE_AFTER_FREE); +} fn required_features() -> wgpu::Features { wgpu::Features::EXPERIMENTAL_RAY_QUERY - | wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE } /// This test creates a blas, puts a reference to it in a tlas instance inside a tlas package, @@ -88,7 +91,7 @@ fn acceleration_structure_use_after_free(ctx: TestingContext) { // Drop the blas and ensure that if it was going to die, it is dead. drop(blas); - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); // build the tlas package to ensure the blas is dropped let mut encoder = ctx @@ -123,7 +126,7 @@ fn acceleration_structure_use_after_free(ctx: TestingContext) { // Drop the TLAS package and ensure that if it was going to die, it is dead. drop(tlas); - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); // Run the pass with the bind group that references the TLAS package. let mut encoder = ctx @@ -147,8 +150,6 @@ static ACCELERATION_STRUCTURE_USE_AFTER_FREE: GpuTestConfiguration = GpuTestConf TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(required_features()) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(required_features()), ) .run_sync(acceleration_structure_use_after_free); diff --git a/tests/tests/wgpu-gpu/ray_tracing/limits.rs b/tests/tests/wgpu-gpu/ray_tracing/limits.rs index a31f5d050f5..c1ee870f83d 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/limits.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/limits.rs @@ -8,7 +8,11 @@ use wgpu::{ ShaderStages, VertexFormat, }; use wgpu_macros::gpu_test; -use wgpu_test::{fail, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{fail, GpuTestConfiguration, TestParameters, TestingContext}; + +pub fn all_tests(tests: &mut Vec) { + tests.push(LIMITS_HIT); +} #[gpu_test] static LIMITS_HIT: GpuTestConfiguration = GpuTestConfiguration::new() @@ -22,9 +26,8 @@ static LIMITS_HIT: GpuTestConfiguration = GpuTestConfiguration::new() max_acceleration_structures_per_shader_stage: 1, ..Limits::default() }) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(hit_limits); diff --git a/tests/tests/wgpu-gpu/ray_tracing/mod.rs b/tests/tests/wgpu-gpu/ray_tracing/mod.rs index b72b3466ca7..4ae2bbd9b0e 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/mod.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/mod.rs @@ -18,6 +18,15 @@ mod limits; mod scene; mod shader; +pub fn all_tests(tests: &mut Vec) { + as_build::all_tests(tests); + as_create::all_tests(tests); + as_use_after_free::all_tests(tests); + limits::all_tests(tests); + scene::all_tests(tests); + shader::all_tests(tests); +} + fn acceleration_structure_limits() -> wgpu::Limits { wgpu::Limits::default().using_minimum_supported_acceleration_structure_values() } diff --git a/tests/tests/wgpu-gpu/ray_tracing/scene/mod.rs b/tests/tests/wgpu-gpu/ray_tracing/scene/mod.rs index 11be74bdf45..9b1760a0c72 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/scene/mod.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/scene/mod.rs @@ -1,6 +1,6 @@ use std::{iter, mem}; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; use wgpu::util::DeviceExt; @@ -9,6 +9,13 @@ use glam::{Affine3A, Quat, Vec3}; mod mesh_gen; +pub fn all_tests(tests: &mut Vec) { + tests.extend([ + ACCELERATION_STRUCTURE_BUILD_NO_INDEX, + ACCELERATION_STRUCTURE_BUILD_WITH_INDEX, + ]); +} + fn acceleration_structure_build(ctx: &TestingContext, use_index_buffer: bool) { let max_instances = 1000; let device = &ctx.device; @@ -94,7 +101,9 @@ fn acceleration_structure_build(ctx: &TestingContext, use_index_buffer: bool) { ctx.queue.submit(Some(encoder.finish())); - ctx.device.poll(wgpu::PollType::Wait).unwrap(); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); } #[gpu_test] @@ -103,9 +112,8 @@ static ACCELERATION_STRUCTURE_BUILD_NO_INDEX: GpuTestConfiguration = GpuTestConf TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(|ctx| { acceleration_structure_build(&ctx, false); @@ -117,9 +125,8 @@ static ACCELERATION_STRUCTURE_BUILD_WITH_INDEX: GpuTestConfiguration = GpuTestCo TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features(wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE) - // https://github.com/gfx-rs/wgpu/issues/6727 - .skip(FailureCase::backend_adapter(wgpu::Backends::VULKAN, "AMD")), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY) + .enable_noop(), ) .run_sync(|ctx| { acceleration_structure_build(&ctx, true); diff --git a/tests/tests/wgpu-gpu/ray_tracing/shader.rs b/tests/tests/wgpu-gpu/ray_tracing/shader.rs index 1ed167e4f15..fcd29af52e6 100644 --- a/tests/tests/wgpu-gpu/ray_tracing/shader.rs +++ b/tests/tests/wgpu-gpu/ray_tracing/shader.rs @@ -5,20 +5,22 @@ use wgpu::{ }; use wgpu::{AccelerationStructureFlags, BufferUsages}; use wgpu_macros::gpu_test; +use wgpu_test::GpuTestInitializer; use wgpu_test::{GpuTestConfiguration, TestParameters, TestingContext}; const STRUCT_SIZE: wgpu::BufferAddress = 176; +pub fn all_tests(tests: &mut Vec) { + tests.push(ACCESS_ALL_STRUCT_MEMBERS); +} + #[gpu_test] static ACCESS_ALL_STRUCT_MEMBERS: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .test_features_limits() .limits(acceleration_structure_limits()) - .features( - wgpu::Features::EXPERIMENTAL_RAY_TRACING_ACCELERATION_STRUCTURE - | wgpu::Features::EXPERIMENTAL_RAY_QUERY, - ), + .features(wgpu::Features::EXPERIMENTAL_RAY_QUERY), ) .run_sync(access_all_struct_members); diff --git a/tests/tests/wgpu-gpu/regression/issue_3349.rs b/tests/tests/wgpu-gpu/regression/issue_3349.rs index 14629636a3f..adee4eb6527 100644 --- a/tests/tests/wgpu-gpu/regression/issue_3349.rs +++ b/tests/tests/wgpu-gpu/regression/issue_3349.rs @@ -1,8 +1,13 @@ use wgpu::util::DeviceExt; use wgpu_test::{ - gpu_test, image::ReadbackBuffers, GpuTestConfiguration, TestParameters, TestingContext, + gpu_test, image::ReadbackBuffers, GpuTestConfiguration, GpuTestInitializer, TestParameters, + TestingContext, }; +pub fn all_tests(vec: &mut Vec) { + vec.push(MULTI_STAGE_DATA_BINDING); +} + /// We thought we had an OpenGL bug that, when running without explicit in-shader locations, /// we will not properly bind uniform buffers to both the vertex and fragment /// shaders. This turned out to not reproduce at all with this test case. diff --git a/tests/tests/wgpu-gpu/regression/issue_3457.rs b/tests/tests/wgpu-gpu/regression/issue_3457.rs index e71e97d844b..495ad029933 100644 --- a/tests/tests/wgpu-gpu/regression/issue_3457.rs +++ b/tests/tests/wgpu-gpu/regression/issue_3457.rs @@ -1,7 +1,11 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; use wgpu::*; +pub fn all_tests(vec: &mut Vec) { + vec.push(PASS_RESET_VERTEX_BUFFER); +} + /// The core issue here was that we weren't properly disabling vertex attributes on GL /// when a renderpass ends. This ended up being rather tricky to test for as GL is remarkably /// tolerant of errors. This test, with the fix not-applied, only fails on WebGL. @@ -15,8 +19,9 @@ use wgpu::*; /// We use non-consecutive vertex attribute locations (0 and 5) in order to also test /// that we unset the correct locations (see PR #3706). #[gpu_test] -static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let module = ctx .device .create_shader_module(include_wgsl!("issue_3457.wgsl")); @@ -167,7 +172,7 @@ static PASS_RESET_VERTEX_BUFFER: GpuTestConfiguration = drop(vertex_buffer2); // Make sure the buffers are actually deleted. - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); let mut encoder2 = ctx .device diff --git a/tests/tests/wgpu-gpu/regression/issue_4024.rs b/tests/tests/wgpu-gpu/regression/issue_4024.rs index 1541f0500ac..9b7d34b202b 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4024.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4024.rs @@ -1,10 +1,14 @@ use std::sync::Arc; use parking_lot::Mutex; -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; use wgpu::*; +pub fn all_tests(vec: &mut Vec) { + vec.push(QUEUE_SUBMITTED_CALLBACK_ORDERING); +} + /// The WebGPU specification has very specific requirements about the ordering of map_async /// and on_submitted_work_done callbacks. Specifically, all map_async callbacks that are initiated /// before a given on_submitted_work_done callback must be invoked before the on_submitted_work_done @@ -14,6 +18,7 @@ use wgpu::*; /// to add them to. This is incorrect, as we do not immediately invoke map_async callbacks. #[gpu_test] static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) .run_async(|ctx| async move { // Create a mappable buffer let buffer = ctx.device.create_buffer(&BufferDescriptor { @@ -36,7 +41,7 @@ static QUEUE_SUBMITTED_CALLBACK_ORDERING: GpuTestConfiguration = GpuTestConfigur // Submit the work. ctx.queue.submit(Some(encoder.finish())); // Ensure the work is finished. - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); #[derive(Debug)] struct OrderingContext { diff --git a/tests/tests/wgpu-gpu/regression/issue_4122.rs b/tests/tests/wgpu-gpu/regression/issue_4122.rs index 27b66e1ae0f..41b0715c49f 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4122.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4122.rs @@ -1,6 +1,12 @@ use std::ops::Range; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(CLEAR_BUFFER_RANGE_RESPECTED); +} async fn fill_test(ctx: &TestingContext, range: Range, size: u64) -> bool { let gpu_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { @@ -32,7 +38,9 @@ async fn fill_test(ctx: &TestingContext, range: Range, size: u64) -> bool { ctx.queue.submit(Some(encoder.finish())); cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let buffer_slice = cpu_buffer.slice(..); let buffer_data = buffer_slice.get_mapped_range(); @@ -85,7 +93,7 @@ async fn fill_test(ctx: &TestingContext, range: Range, size: u64) -> bool { /// This test will fail on nvidia if the bug is not properly worked around. #[gpu_test] static CLEAR_BUFFER_RANGE_RESPECTED: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_async(|ctx| async move { // This hits most of the cases in nvidia's clear buffer bug let mut succeeded = true; diff --git a/tests/tests/wgpu-gpu/regression/issue_4485.rs b/tests/tests/wgpu-gpu/regression/issue_4485.rs index 9d6c83955fa..5c814377822 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4485.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4485.rs @@ -1,4 +1,10 @@ -use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, image, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(CONTINUE_SWITCH); +} /// FXC doesn't accept `continue` inside a switch. Instead we store a flag for whether /// the loop should continue that is checked after the switch. @@ -10,7 +16,7 @@ use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestParameters, TestingCo /// This also tests that shaders generated with this fix execute correctly. #[gpu_test] static CONTINUE_SWITCH: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().force_fxc(true)) + .parameters(TestParameters::default().force_fxc(true).enable_noop()) .run_async(|ctx| async move { test_impl(&ctx).await }); async fn test_impl(ctx: &TestingContext) { diff --git a/tests/tests/wgpu-gpu/regression/issue_4514.rs b/tests/tests/wgpu-gpu/regression/issue_4514.rs index b523dcd330b..f3ad2051a90 100644 --- a/tests/tests/wgpu-gpu/regression/issue_4514.rs +++ b/tests/tests/wgpu-gpu/regression/issue_4514.rs @@ -1,4 +1,10 @@ -use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, image, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(DEGENERATE_SWITCH); +} /// FXC and potentially some glsl consumers have a bug when handling switch statements on a constant /// with just a default case. (not sure if the constant part is relevant) @@ -10,7 +16,7 @@ use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestParameters, TestingCo /// bug is avoided there. #[gpu_test] static DEGENERATE_SWITCH: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().force_fxc(true)) + .parameters(TestParameters::default().force_fxc(true).enable_noop()) .run_async(|ctx| async move { test_impl(&ctx).await }); async fn test_impl(ctx: &TestingContext) { diff --git a/tests/tests/wgpu-gpu/regression/issue_5553.rs b/tests/tests/wgpu-gpu/regression/issue_5553.rs index 01ffb59d1a2..7c1884dc3a9 100644 --- a/tests/tests/wgpu-gpu/regression/issue_5553.rs +++ b/tests/tests/wgpu-gpu/regression/issue_5553.rs @@ -1,7 +1,11 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; use wgpu::*; +pub fn all_tests(vec: &mut Vec) { + vec.push(ALLOW_INPUT_NOT_CONSUMED); +} + /// Previously, for every user-defined vertex output a fragment shader had to have a corresponding /// user-defined input. This would generate `StageError::InputNotConsumed`. /// @@ -10,8 +14,9 @@ use wgpu::*; /// the fragment inputs. This is necessary for generating correct hlsl: /// https://github.com/gfx-rs/wgpu/issues/5553 #[gpu_test] -static ALLOW_INPUT_NOT_CONSUMED: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static ALLOW_INPUT_NOT_CONSUMED: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let module = ctx .device .create_shader_module(include_wgsl!("issue_5553.wgsl")); diff --git a/tests/tests/wgpu-gpu/regression/issue_6317.rs b/tests/tests/wgpu-gpu/regression/issue_6317.rs index 20945006f74..fd42885be62 100644 --- a/tests/tests/wgpu-gpu/regression/issue_6317.rs +++ b/tests/tests/wgpu-gpu/regression/issue_6317.rs @@ -1,13 +1,18 @@ use wgpu::{DownlevelFlags, Limits}; use wgpu_macros::gpu_test; -use wgpu_test::{fail, GpuTestConfiguration, TestParameters}; +use wgpu_test::{fail, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(NON_FATAL_ERRORS_IN_QUEUE_SUBMIT); +} #[gpu_test] static NON_FATAL_ERRORS_IN_QUEUE_SUBMIT: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .downlevel_flags(DownlevelFlags::COMPUTE_SHADERS) - .limits(Limits::downlevel_defaults()), + .limits(Limits::downlevel_defaults()) + .enable_noop(), ) .run_sync(|ctx| { let shader_with_trivial_bind_group = concat!( diff --git a/tests/tests/wgpu-gpu/regression/issue_6467.rs b/tests/tests/wgpu-gpu/regression/issue_6467.rs index 75ccc43a993..84d680d012c 100644 --- a/tests/tests/wgpu-gpu/regression/issue_6467.rs +++ b/tests/tests/wgpu-gpu/regression/issue_6467.rs @@ -1,5 +1,9 @@ use wgpu::util::DeviceExt; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(ZERO_WORKGROUP_COUNT); +} /// Running a compute shader with a total workgroup count of zero implies that no work /// should be done, and is a user error. Vulkan and DX12 accept this invalid input with grace, but @@ -9,7 +13,11 @@ use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; /// The following test should successfully do nothing on all platforms. #[gpu_test] static ZERO_WORKGROUP_COUNT: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().limits(wgpu::Limits::default())) + .parameters( + TestParameters::default() + .limits(wgpu::Limits::default()) + .enable_noop(), + ) .run_async(|ctx| async move { let module = ctx .device diff --git a/tests/tests/wgpu-gpu/regression/issue_6827.rs b/tests/tests/wgpu-gpu/regression/issue_6827.rs index 2cb1bbd0392..97ad32be412 100644 --- a/tests/tests/wgpu-gpu/regression/issue_6827.rs +++ b/tests/tests/wgpu-gpu/regression/issue_6827.rs @@ -1,6 +1,12 @@ use std::sync::Arc; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(tests: &mut Vec) { + tests.extend([TEST_SINGLE_WRITE, TEST_SCATTER]); +} #[gpu_test] static TEST_SINGLE_WRITE: GpuTestConfiguration = GpuTestConfiguration::new() @@ -19,17 +25,7 @@ static TEST_SCATTER: GpuTestConfiguration = GpuTestConfiguration::new() .expect_fail(FailureCase::backend_adapter( wgpu::Backends::METAL, "Apple Paravirtual device", // CI on M1 - )) - .expect_fail( - // Unfortunately this depends on if `D3D12_FEATURE_DATA_D3D12_OPTIONS13.UnrestrictedBufferTextureCopyPitchSupported` - // is true, which we have no way to encode. This reproduces in CI though, so not too worried about it. - FailureCase::backend(wgpu::Backends::DX12) - .flaky() - .validation_error( - "D3D12_PLACED_SUBRESOURCE_FOOTPRINT::Offset must be a multiple of 512", - ) - .panic("GraphicsCommandList::close failed: The parameter is incorrect"), - ), + )), ) .run_async(|ctx| async move { run_test(ctx, true).await }); @@ -73,7 +69,7 @@ async fn run_test(ctx: TestingContext, use_many_writes: bool) { let result_cell = result_cell.clone(); move |result| result_cell.set(result).unwrap() }); - device.poll(wgpu::PollType::Wait).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); result_cell .get() .as_ref() diff --git a/tests/tests/wgpu-gpu/render_pass_ownership.rs b/tests/tests/wgpu-gpu/render_pass_ownership.rs index e3574e3760b..8d1f65972e5 100644 --- a/tests/tests/wgpu-gpu/render_pass_ownership.rs +++ b/tests/tests/wgpu-gpu/render_pass_ownership.rs @@ -12,7 +12,18 @@ use std::num::NonZeroU64; use wgpu::util::DeviceExt as _; -use wgpu_test::{gpu_test, valid, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + RENDER_PASS_RESOURCE_OWNERSHIP, + RENDER_PASS_QUERY_SET_OWNERSHIP_PIPELINE_STATISTICS, + RENDER_PASS_QUERY_SET_OWNERSHIP_TIMESTAMPS, + RENDER_PASS_KEEP_ENCODER_ALIVE, + ]); +} // Minimal shader with buffer based side effect - only needed to check whether the render pass has executed at all. const SHADER_SRC: &str = " @@ -102,7 +113,9 @@ async fn render_pass_resource_ownership(ctx: TestingContext) { drop(vertex_buffer); drop(index_buffer); drop(occlusion_query_set); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await; @@ -172,7 +185,9 @@ async fn render_pass_query_set_ownership_pipeline_statistics(ctx: TestingContext // Drop the query set. Then do a device poll to make sure it's not dropped too early, no matter what. drop(query_set); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await; @@ -249,7 +264,9 @@ async fn render_pass_query_set_ownership_timestamps(ctx: TestingContext) { // Drop the query sets. Then do a device poll to make sure they're not dropped too early, no matter what. drop(query_set_timestamp_writes); drop(query_set_write_timestamp); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); } assert_render_pass_executed_normally(encoder, gpu_buffer, cpu_buffer, buffer_size, ctx).await; @@ -257,7 +274,11 @@ async fn render_pass_query_set_ownership_timestamps(ctx: TestingContext) { #[gpu_test] static RENDER_PASS_KEEP_ENCODER_ALIVE: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().test_features_limits()) + .parameters( + TestParameters::default() + .test_features_limits() + .enable_noop(), + ) .run_async(render_pass_keep_encoder_alive); async fn render_pass_keep_encoder_alive(ctx: TestingContext) { @@ -297,7 +318,9 @@ async fn render_pass_keep_encoder_alive(ctx: TestingContext) { let mut rpass = rpass.forget_lifetime(); drop(encoder); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); // Record some a draw command. rpass.set_pipeline(&pipeline); @@ -323,7 +346,9 @@ async fn assert_render_pass_executed_normally( encoder.copy_buffer_to_buffer(&gpu_buffer, 0, &cpu_buffer, 0, buffer_size); ctx.queue.submit([encoder.finish()]); cpu_buffer.slice(..).map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = cpu_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/render_target.rs b/tests/tests/wgpu-gpu/render_target.rs index 9c4723b6af1..e45b8630a4e 100644 --- a/tests/tests/wgpu-gpu/render_target.rs +++ b/tests/tests/wgpu-gpu/render_target.rs @@ -2,7 +2,19 @@ use wgpu::{ util::{BufferInitDescriptor, DeviceExt}, vertex_attr_array, }; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + DRAW_TO_2D_VIEW, + DRAW_TO_2D_ARRAY_VIEW, + RESOLVE_TO_2D_VIEW, + RESOLVE_TO_2D_ARRAY_VIEW, + DRAW_TO_3D_VIEW, + ]); +} #[gpu_test] static DRAW_TO_2D_VIEW: GpuTestConfiguration = GpuTestConfiguration::new() @@ -227,7 +239,9 @@ async fn run_test( let slice = readback_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = slice.get_mapped_range(); let succeeded = data.iter().all(|b| *b == u8::MAX); @@ -408,7 +422,9 @@ async fn run_test_3d(ctx: TestingContext) { let slice = readback_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data = slice.get_mapped_range(); let succeeded = data.iter().all(|b| *b == u8::MAX); diff --git a/tests/tests/wgpu-gpu/resource_descriptor_accessor.rs b/tests/tests/wgpu-gpu/resource_descriptor_accessor.rs index ee984da60e4..5a25ab0cb98 100644 --- a/tests/tests/wgpu-gpu/resource_descriptor_accessor.rs +++ b/tests/tests/wgpu-gpu/resource_descriptor_accessor.rs @@ -1,17 +1,23 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(BUFFER_SIZE_AND_USAGE); +} #[gpu_test] -static BUFFER_SIZE_AND_USAGE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 1234, - usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, - mapped_at_creation: false, - }); +static BUFFER_SIZE_AND_USAGE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + let buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1234, + usage: wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); - assert_eq!(buffer.size(), 1234); - assert_eq!( - buffer.usage(), - wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST - ); -}); + assert_eq!(buffer.size(), 1234); + assert_eq!( + buffer.usage(), + wgpu::BufferUsages::COPY_SRC | wgpu::BufferUsages::COPY_DST + ); + }); diff --git a/tests/tests/wgpu-gpu/resource_error.rs b/tests/tests/wgpu-gpu/resource_error.rs index d071053ebd1..e8f9235498a 100644 --- a/tests/tests/wgpu-gpu/resource_error.rs +++ b/tests/tests/wgpu-gpu/resource_error.rs @@ -1,66 +1,74 @@ -use wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration}; +use wgpu_test::{fail, gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([BAD_BUFFER, BAD_TEXTURE]); +} #[gpu_test] -static BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - // Create a buffer with bad parameters and call a few methods. - // Validation should fail but there should be not panic. - let buffer = fail( - &ctx.device, - || { - ctx.device.create_buffer(&wgpu::BufferDescriptor { - label: None, - size: 99999999, - usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE, - mapped_at_creation: false, - }) - }, - Some("`map` usage can only be combined with the opposite `copy`"), - ); +static BAD_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + // Create a buffer with bad parameters and call a few methods. + // Validation should fail but there should be not panic. + let buffer = fail( + &ctx.device, + || { + ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 99999999, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::STORAGE, + mapped_at_creation: false, + }) + }, + Some("`map` usage can only be combined with the opposite `copy`"), + ); - fail( - &ctx.device, - || buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {}), - Some("Buffer with '' label is invalid"), - ); - fail( - &ctx.device, - || buffer.unmap(), - Some("Buffer with '' label is invalid"), - ); - valid(&ctx.device, || buffer.destroy()); - valid(&ctx.device, || buffer.destroy()); -}); + fail( + &ctx.device, + || buffer.slice(..).map_async(wgpu::MapMode::Write, |_| {}), + Some("Buffer with '' label is invalid"), + ); + fail( + &ctx.device, + || buffer.unmap(), + Some("Buffer with '' label is invalid"), + ); + valid(&ctx.device, || buffer.destroy()); + valid(&ctx.device, || buffer.destroy()); + }); #[gpu_test] -static BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - let texture = fail( - &ctx.device, - || { - ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: 0, - height: 12345678, - depth_or_array_layers: 9001, - }, - mip_level_count: 2000, - sample_count: 27, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8UnormSrgb, - usage: wgpu::TextureUsages::all(), - view_formats: &[], - }) - }, - Some("dimension x is zero"), - ); +static BAD_TEXTURE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + let texture = fail( + &ctx.device, + || { + ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 0, + height: 12345678, + depth_or_array_layers: 9001, + }, + mip_level_count: 2000, + sample_count: 27, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::all(), + view_formats: &[], + }) + }, + Some("dimension x is zero"), + ); - fail( - &ctx.device, - || { - let _ = texture.create_view(&wgpu::TextureViewDescriptor::default()); - }, - Some("Texture with '' label is invalid"), - ); - valid(&ctx.device, || texture.destroy()); - valid(&ctx.device, || texture.destroy()); -}); + fail( + &ctx.device, + || { + let _ = texture.create_view(&wgpu::TextureViewDescriptor::default()); + }, + Some("Texture with '' label is invalid"), + ); + valid(&ctx.device, || texture.destroy()); + valid(&ctx.device, || texture.destroy()); + }); diff --git a/tests/tests/wgpu-gpu/samplers.rs b/tests/tests/wgpu-gpu/samplers.rs index 97e57a55f7a..6c8018302d5 100644 --- a/tests/tests/wgpu-gpu/samplers.rs +++ b/tests/tests/wgpu-gpu/samplers.rs @@ -2,15 +2,28 @@ //! //! Do some tests to ensure things are working correctly and nothing gets mad. -use wgpu_test::{did_oom, gpu_test, valid, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + did_oom, gpu_test, valid, GpuTestConfiguration, GpuTestInitializer, TestParameters, + TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + SAMPLER_DEDUPLICATION, + SAMPLER_CREATION_FAILURE, + SAMPLER_SINGLE_BIND_GROUP, + SAMPLER_MULTI_BIND_GROUP, + ]); +} // A number large enough to likely cause sampler caches to run out of space // on some devices. const PROBABLY_PROBLEMATIC_SAMPLER_COUNT: u32 = 8 * 1024; #[gpu_test] -static SAMPLER_DEDUPLICATION: GpuTestConfiguration = - GpuTestConfiguration::new().run_sync(sampler_deduplication); +static SAMPLER_DEDUPLICATION: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(sampler_deduplication); // Create a large number of samplers from the same two descriptors. // @@ -59,8 +72,9 @@ fn sampler_deduplication(ctx: TestingContext) { } #[gpu_test] -static SAMPLER_CREATION_FAILURE: GpuTestConfiguration = - GpuTestConfiguration::new().run_sync(sampler_creation_failure); +static SAMPLER_CREATION_FAILURE: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(sampler_creation_failure); /// We want to test that sampler creation properly fails when we hit internal sampler /// cache limits. As we don't actually know what the limit is, we first create as many @@ -110,11 +124,13 @@ fn sampler_creation_failure(ctx: TestingContext) { let failed_count = sampler_storage.len(); sampler_storage.clear(); - ctx.device.poll(wgpu::PollType::Wait).unwrap(); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); for i in 0..failed_count { valid(&ctx.device, || { - eprintln!("Trying to create sampler {}", i); + eprintln!("Trying to create sampler {i}"); let sampler = ctx.device.create_sampler(&wgpu::SamplerDescriptor { lod_min_clamp: i as f32 * 0.01, // Change the max clamp to ensure the sampler is using different cache slots from @@ -188,7 +204,7 @@ fn sampler_bind_group(ctx: TestingContext, group_type: GroupType) { GroupType::Multi => MULTI_GROUP_BINDINGS, }; - let full_shader = format!("{}\n{}", bindings, SAMPLER_CODE); + let full_shader = format!("{bindings}\n{SAMPLER_CODE}"); let module = ctx .device @@ -525,7 +541,9 @@ fn sampler_bind_group(ctx: TestingContext, group_type: GroupType) { let buffer_slice = transfer_buffer.slice(..); buffer_slice.map_async(wgpu::MapMode::Read, |_| {}); - ctx.device.poll(wgpu::PollType::Wait).unwrap(); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); let buffer_data = buffer_slice.get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/scissor_tests/mod.rs b/tests/tests/wgpu-gpu/scissor_tests/mod.rs index ab958daf312..14b81eb4337 100644 --- a/tests/tests/wgpu-gpu/scissor_tests/mod.rs +++ b/tests/tests/wgpu-gpu/scissor_tests/mod.rs @@ -1,4 +1,13 @@ -use wgpu_test::{gpu_test, image, GpuTestConfiguration, TestingContext}; +use wgpu_test::{gpu_test, image, GpuTestConfiguration, GpuTestInitializer, TestingContext}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + SCISSOR_TEST_FULL_RECT, + SCISSOR_TEST_EMPTY_RECT, + SCISSOR_TEST_EMPTY_RECT_WITH_OFFSET, + SCISSOR_TEST_CUSTOM_RECT, + ]); +} struct Rect { x: u32, diff --git a/tests/tests/wgpu-gpu/shader/array_size_overrides.rs b/tests/tests/wgpu-gpu/shader/array_size_overrides.rs index a39db5d17c7..531f70c597c 100644 --- a/tests/tests/wgpu-gpu/shader/array_size_overrides.rs +++ b/tests/tests/wgpu-gpu/shader/array_size_overrides.rs @@ -3,6 +3,10 @@ use wgpu::util::DeviceExt; use wgpu::{BufferDescriptor, BufferUsages, MapMode, PollType}; use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.push(ARRAY_SIZE_OVERRIDES); +} + const SHADER: &str = r#" const testing_shared: array = array(8); override n = testing_shared[0u]; @@ -140,7 +144,7 @@ async fn array_size_overrides( ctx.queue.submit(Some(encoder.finish())); mapping_buffer.slice(..).map_async(MapMode::Read, |_| ()); - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); let mapped = mapping_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/shader/compilation_messages/mod.rs b/tests/tests/wgpu-gpu/shader/compilation_messages/mod.rs index 09000205a24..9304836a15e 100644 --- a/tests/tests/wgpu-gpu/shader/compilation_messages/mod.rs +++ b/tests/tests/wgpu-gpu/shader/compilation_messages/mod.rs @@ -1,10 +1,14 @@ use wgpu::include_wgsl; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([SHADER_COMPILE_SUCCESS, SHADER_COMPILE_ERROR]); +} #[gpu_test] static SHADER_COMPILE_SUCCESS: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_async(|ctx| async move { let sm = ctx .device @@ -18,7 +22,7 @@ static SHADER_COMPILE_SUCCESS: GpuTestConfiguration = GpuTestConfiguration::new( #[gpu_test] static SHADER_COMPILE_ERROR: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default()) + .parameters(TestParameters::default().enable_noop()) .run_async(|ctx| async move { ctx.device.push_error_scope(wgpu::ErrorFilter::Validation); let sm = ctx diff --git a/tests/tests/wgpu-gpu/shader/data_builtins.rs b/tests/tests/wgpu-gpu/shader/data_builtins.rs index f6f24f32419..d17691b9425 100644 --- a/tests/tests/wgpu-gpu/shader/data_builtins.rs +++ b/tests/tests/wgpu-gpu/shader/data_builtins.rs @@ -1,7 +1,11 @@ use wgpu::{DownlevelFlags, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest}; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([UNPACK4xU8, UNPACK4xI8, PACK4xU8, PACK4xI8]); +} #[allow(non_snake_case)] fn create_unpack4xU8_test() -> Vec { diff --git a/tests/tests/wgpu-gpu/shader/mod.rs b/tests/tests/wgpu-gpu/shader/mod.rs index 15d4c5e5cf7..6c8a4637f17 100644 --- a/tests/tests/wgpu-gpu/shader/mod.rs +++ b/tests/tests/wgpu-gpu/shader/mod.rs @@ -13,7 +13,7 @@ use wgpu::{ ShaderModuleDescriptor, ShaderSource, ShaderStages, }; -use wgpu_test::TestingContext; +use wgpu_test::{GpuTestInitializer, TestingContext}; pub mod array_size_overrides; pub mod compilation_messages; @@ -23,6 +23,16 @@ pub mod struct_layout; pub mod workgroup_size_overrides; pub mod zero_init_workgroup_mem; +pub fn all_tests(tests: &mut Vec) { + array_size_overrides::all_tests(tests); + compilation_messages::all_tests(tests); + data_builtins::all_tests(tests); + numeric_builtins::all_tests(tests); + struct_layout::all_tests(tests); + workgroup_size_overrides::all_tests(tests); + zero_init_workgroup_mem::all_tests(tests); +} + #[derive(Clone, Copy, PartialEq)] enum InputStorageType { Uniform, @@ -367,7 +377,7 @@ async fn shader_input_output_test( ctx.queue.submit(Some(encoder.finish())); mapping_buffer.slice(..).map_async(MapMode::Read, |_| ()); - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); let mapped = mapping_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/shader/numeric_builtins.rs b/tests/tests/wgpu-gpu/shader/numeric_builtins.rs index bbdd379ba0d..1416f9b378a 100644 --- a/tests/tests/wgpu-gpu/shader/numeric_builtins.rs +++ b/tests/tests/wgpu-gpu/shader/numeric_builtins.rs @@ -1,7 +1,16 @@ use wgpu::{DownlevelFlags, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest}; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + NUMERIC_BUILTINS, + INT64_ATOMIC_MIN_MAX, + INT64_ATOMIC_ALL_OPS, + FLOAT32_ATOMIC, + ]); +} fn create_numeric_builtin_test() -> Vec { let mut tests = Vec::new(); diff --git a/tests/tests/wgpu-gpu/shader/struct_layout.rs b/tests/tests/wgpu-gpu/shader/struct_layout.rs index e9378f4012d..8cb4501af5d 100644 --- a/tests/tests/wgpu-gpu/shader/struct_layout.rs +++ b/tests/tests/wgpu-gpu/shader/struct_layout.rs @@ -3,7 +3,20 @@ use std::fmt::Write; use wgpu::{Backends, DownlevelFlags, Features, Limits}; use crate::shader::{shader_input_output_test, InputStorageType, ShaderTest, MAX_BUFFER_SIZE}; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + UNIFORM_INPUT, + STORAGE_INPUT, + PUSH_CONSTANT_INPUT, + UNIFORM_INPUT_INT64, + STORAGE_INPUT_INT64, + PUSH_CONSTANT_INPUT_INT64, + UNIFORM_INPUT_F16, + STORAGE_INPUT_F16, + ]); +} #[gpu_test] static UNIFORM_INPUT: GpuTestConfiguration = GpuTestConfiguration::new() diff --git a/tests/tests/wgpu-gpu/shader/workgroup_size_overrides.rs b/tests/tests/wgpu-gpu/shader/workgroup_size_overrides.rs index d61760707f0..3d48ca2a8ff 100644 --- a/tests/tests/wgpu-gpu/shader/workgroup_size_overrides.rs +++ b/tests/tests/wgpu-gpu/shader/workgroup_size_overrides.rs @@ -3,6 +3,10 @@ use wgpu::util::DeviceExt; use wgpu::{BufferDescriptor, BufferUsages, MapMode, PollType}; use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.push(WORKGROUP_SIZE_OVERRIDES); +} + const SHADER: &str = r#" override n = 3; @@ -107,7 +111,7 @@ async fn workgroup_size_overrides( ctx.queue.submit(Some(encoder.finish())); mapping_buffer.slice(..).map_async(MapMode::Read, |_| ()); - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); let mapped = mapping_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/shader/zero_init_workgroup_mem.rs b/tests/tests/wgpu-gpu/shader/zero_init_workgroup_mem.rs index 1aaf7341d17..7192aac8f99 100644 --- a/tests/tests/wgpu-gpu/shader/zero_init_workgroup_mem.rs +++ b/tests/tests/wgpu-gpu/shader/zero_init_workgroup_mem.rs @@ -8,7 +8,11 @@ use wgpu::{ ShaderStages, }; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(ZERO_INIT_WORKGROUP_MEMORY); +} #[gpu_test] static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration::new() @@ -131,7 +135,7 @@ static ZERO_INIT_WORKGROUP_MEMORY: GpuTestConfiguration = GpuTestConfiguration:: ctx.queue.submit(Some(encoder.finish())); mapping_buffer.slice(..).map_async(MapMode::Read, |_| ()); - ctx.async_poll(PollType::wait()).await.unwrap(); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); let mapped = mapping_buffer.slice(..).get_mapped_range(); diff --git a/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs b/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs index 83832c01d7e..eb6e6fa1d23 100644 --- a/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs +++ b/tests/tests/wgpu-gpu/shader_primitive_index/mod.rs @@ -1,6 +1,10 @@ use wgpu::util::DeviceExt; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.extend([DRAW, DRAW_INDEXED]); +} + // // These tests render two triangles to a 2x2 render target. The first triangle // in the vertex buffer covers the bottom-left pixel, the second triangle diff --git a/tests/tests/wgpu-gpu/shader_view_format/mod.rs b/tests/tests/wgpu-gpu/shader_view_format/mod.rs index 849e050b855..0087ff35adc 100644 --- a/tests/tests/wgpu-gpu/shader_view_format/mod.rs +++ b/tests/tests/wgpu-gpu/shader_view_format/mod.rs @@ -1,6 +1,10 @@ use wgpu::{util::DeviceExt, DownlevelFlags, Limits, TextureFormat}; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.push(REINTERPRET_SRGB); +} + #[gpu_test] static REINTERPRET_SRGB: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( @@ -185,7 +189,9 @@ async fn reinterpret( let slice = read_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data: Vec = slice.get_mapped_range().to_vec(); let tolerance_data: [[u8; 4]; 4] = [[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 1, 0], [1, 1, 1, 0]]; diff --git a/tests/tests/wgpu-gpu/subgroup_operations/mod.rs b/tests/tests/wgpu-gpu/subgroup_operations/mod.rs index 7e50ea5051b..41f444da80a 100644 --- a/tests/tests/wgpu-gpu/subgroup_operations/mod.rs +++ b/tests/tests/wgpu-gpu/subgroup_operations/mod.rs @@ -1,6 +1,10 @@ use std::num::NonZeroU64; -use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(SUBGROUP_OPERATIONS); +} const THREAD_COUNT: u64 = 128; const TEST_COUNT: u32 = 37; diff --git a/tests/tests/wgpu-gpu/texture_binding/mod.rs b/tests/tests/wgpu-gpu/texture_binding/mod.rs index 74be773a6f1..62b16e38530 100644 --- a/tests/tests/wgpu-gpu/texture_binding/mod.rs +++ b/tests/tests/wgpu-gpu/texture_binding/mod.rs @@ -7,14 +7,19 @@ use wgpu::{ TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }; use wgpu_macros::gpu_test; -use wgpu_test::{GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([TEXTURE_BINDING, SINGLE_SCALAR_LOAD]); +} #[gpu_test] static TEXTURE_BINDING: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .test_features_limits() - .downlevel_flags(DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT), + .downlevel_flags(DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT) + .enable_noop(), ) .run_sync(texture_binding); @@ -176,7 +181,7 @@ fn single_scalar_load(ctx: TestingContext) { send.send(()).expect("Thread should wait for receive"); }); // Poll to run map. - ctx.device.poll(PollType::Wait).unwrap(); + ctx.device.poll(PollType::wait_indefinitely()).unwrap(); recv.recv_timeout(Duration::from_secs(10)) .expect("mapping should not take this long"); let val = *bytemuck::from_bytes::<[f32; 4]>(&buffer.slice(..).get_mapped_range()); diff --git a/tests/tests/wgpu-gpu/texture_blit.rs b/tests/tests/wgpu-gpu/texture_blit.rs index 6cc30cdaecd..e2b0a6c6e8a 100644 --- a/tests/tests/wgpu-gpu/texture_blit.rs +++ b/tests/tests/wgpu-gpu/texture_blit.rs @@ -1,7 +1,15 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + TEXTURE_BLIT_WITH_LINEAR_FILTER_TEST, + TEXTURE_BLIT_WITH_NEAREST_FILTER_TEST, + ]); +} #[gpu_test] static TEXTURE_BLIT_WITH_LINEAR_FILTER_TEST: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let source = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, @@ -53,6 +61,7 @@ static TEXTURE_BLIT_WITH_LINEAR_FILTER_TEST: GpuTestConfiguration = GpuTestConfi #[gpu_test] static TEXTURE_BLIT_WITH_NEAREST_FILTER_TEST: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) .run_sync(|ctx| { let source = ctx.device.create_texture(&wgpu::TextureDescriptor { label: None, diff --git a/tests/tests/wgpu-gpu/texture_bounds.rs b/tests/tests/wgpu-gpu/texture_bounds.rs index 01e34d1317e..561e3635471 100644 --- a/tests/tests/wgpu-gpu/texture_bounds.rs +++ b/tests/tests/wgpu-gpu/texture_bounds.rs @@ -1,91 +1,97 @@ //! Tests for texture copy bounds checks. -use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration}; +use wgpu_test::{fail_if, gpu_test, GpuTestConfiguration, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(BAD_COPY_ORIGIN_TEST); +} #[gpu_test] -static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - let try_origin = |origin, size, should_panic| { - let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR); - let data = vec![255; BUFFER_SIZE as usize]; +static BAD_COPY_ORIGIN_TEST: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + let try_origin = |origin, size, should_panic| { + let texture = ctx.device.create_texture(&TEXTURE_DESCRIPTOR); + let data = vec![255; BUFFER_SIZE as usize]; - fail_if( - &ctx.device, - should_panic, - || { - ctx.queue.write_texture( - wgpu::TexelCopyTextureInfo { - texture: &texture, - mip_level: 0, - origin, - aspect: wgpu::TextureAspect::All, - }, - &data, - BUFFER_COPY_LAYOUT, - size, - ) - }, - None, - ); - }; + fail_if( + &ctx.device, + should_panic, + || { + ctx.queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin, + aspect: wgpu::TextureAspect::All, + }, + &data, + BUFFER_COPY_LAYOUT, + size, + ) + }, + None, + ); + }; - try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false); - try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true); - try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 0, z: 0 }, TEXTURE_SIZE, false); + try_origin(wgpu::Origin3d { x: 1, y: 0, z: 0 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 1, z: 0 }, TEXTURE_SIZE, true); + try_origin(wgpu::Origin3d { x: 0, y: 0, z: 1 }, TEXTURE_SIZE, true); - try_origin( - wgpu::Origin3d { - x: TEXTURE_SIZE.width - 1, - y: TEXTURE_SIZE.height - 1, - z: TEXTURE_SIZE.depth_or_array_layers - 1, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - false, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); - try_origin( - wgpu::Origin3d { - x: u32::MAX, - y: 0, - z: 0, - }, - wgpu::Extent3d { - width: 1, - height: 1, - depth_or_array_layers: 1, - }, - true, - ); -}); + try_origin( + wgpu::Origin3d { + x: TEXTURE_SIZE.width - 1, + y: TEXTURE_SIZE.height - 1, + z: TEXTURE_SIZE.depth_or_array_layers - 1, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + false, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + try_origin( + wgpu::Origin3d { + x: u32::MAX, + y: 0, + z: 0, + }, + wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 1, + }, + true, + ); + }); const TEXTURE_SIZE: wgpu::Extent3d = wgpu::Extent3d { width: 64, diff --git a/tests/tests/wgpu-gpu/texture_view_creation.rs b/tests/tests/wgpu-gpu/texture_view_creation.rs index d6880fb6203..5bf7ae1fa94 100644 --- a/tests/tests/wgpu-gpu/texture_view_creation.rs +++ b/tests/tests/wgpu-gpu/texture_view_creation.rs @@ -1,12 +1,21 @@ use wgpu::*; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters}; +use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + STENCIL_ONLY_VIEW_CREATION, + DEPTH_ONLY_VIEW_CREATION, + SHARED_USAGE_VIEW_CREATION, + ]); +} #[gpu_test] static STENCIL_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new() .parameters( TestParameters::default() .skip(FailureCase::webgl2()) // WebGL doesn't have stencil only views - .limits(wgpu::Limits::downlevel_defaults()), + .limits(wgpu::Limits::downlevel_defaults()) + .enable_noop(), ) .run_async(|ctx| async move { for format in [TextureFormat::Stencil8, TextureFormat::Depth24PlusStencil8] { @@ -34,8 +43,9 @@ static STENCIL_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration:: }); #[gpu_test] -static DEPTH_ONLY_VIEW_CREATION: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static DEPTH_ONLY_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { for format in [ TextureFormat::Depth16Unorm, TextureFormat::Depth24Plus, @@ -66,7 +76,11 @@ static DEPTH_ONLY_VIEW_CREATION: GpuTestConfiguration = #[gpu_test] static SHARED_USAGE_VIEW_CREATION: GpuTestConfiguration = GpuTestConfiguration::new() - .parameters(TestParameters::default().downlevel_flags(DownlevelFlags::VIEW_FORMATS)) + .parameters( + TestParameters::default() + .downlevel_flags(DownlevelFlags::VIEW_FORMATS) + .enable_noop(), + ) .run_async(|ctx| async move { { let (texture_format, view_format) = diff --git a/tests/tests/wgpu-gpu/timestamp_normalization/mod.rs b/tests/tests/wgpu-gpu/timestamp_normalization/mod.rs index 95adf51c882..9c793028c2f 100644 --- a/tests/tests/wgpu-gpu/timestamp_normalization/mod.rs +++ b/tests/tests/wgpu-gpu/timestamp_normalization/mod.rs @@ -1 +1,5 @@ mod utils; + +pub fn all_tests(tests: &mut Vec) { + utils::all_tests(tests); +} diff --git a/tests/tests/wgpu-gpu/timestamp_normalization/utils.rs b/tests/tests/wgpu-gpu/timestamp_normalization/utils.rs index aaf0548fa68..10ee50c2019 100644 --- a/tests/tests/wgpu-gpu/timestamp_normalization/utils.rs +++ b/tests/tests/wgpu-gpu/timestamp_normalization/utils.rs @@ -8,6 +8,10 @@ use nanorand::Rng; use wgpu::{util::DeviceExt, Limits}; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.extend([U64_MUL_U32, SHIFT_RIGHT_U96]); +} + #[repr(C)] #[derive(Debug, Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)] struct Uint96(u32, u32, u32); @@ -51,8 +55,7 @@ fn assert_u64_mul_u32(left: u64, right: u32, computed: Uint96) { assert_eq!( computed, real, - "{left} * {right} should be {real} but is {}", - computed + "{left} * {right} should be {real} but is {computed}" ); } @@ -277,9 +280,9 @@ fn process_shader(ctx: TestingContext, inputs: &[u8], entry_point_src: &str) -> ctx.queue.submit([encoder.finish()]); pulldown_buffer.map_async(wgpu::MapMode::Read, .., |_| {}); - ctx.device.poll(wgpu::PollType::Wait).unwrap(); - - let value = pulldown_buffer.get_mapped_range(..).to_vec(); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); - value + pulldown_buffer.get_mapped_range(..).to_vec() } diff --git a/tests/tests/wgpu-gpu/timestamp_query.rs b/tests/tests/wgpu-gpu/timestamp_query.rs index ed2ae8d9277..a19bcf2f182 100644 --- a/tests/tests/wgpu-gpu/timestamp_query.rs +++ b/tests/tests/wgpu-gpu/timestamp_query.rs @@ -2,7 +2,13 @@ use wgpu::{ util::DeviceExt, ComputePassTimestampWrites, Features, InstanceFlags, QUERY_RESOLVE_BUFFER_ALIGNMENT, }; -use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +use wgpu_test::{ + gpu_test, FailureCase, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(TIMESTAMP_QUERY); +} const SHADER: &str = r#" @compute @workgroup_size(1) @@ -115,7 +121,9 @@ fn timestamp_query(ctx: TestingContext) { mapping_buffer .slice(..) .map_async(wgpu::MapMode::Read, |_| ()); - ctx.device.poll(wgpu::PollType::wait()).unwrap(); + ctx.device + .poll(wgpu::PollType::wait_indefinitely()) + .unwrap(); let query_buffer_view = mapping_buffer.slice(..).get_mapped_range(); let query_data: &[u64] = bytemuck::cast_slice(&query_buffer_view); diff --git a/tests/tests/wgpu-gpu/transfer.rs b/tests/tests/wgpu-gpu/transfer.rs index cce4b0f7d60..0e2adbbc2c0 100644 --- a/tests/tests/wgpu-gpu/transfer.rs +++ b/tests/tests/wgpu-gpu/transfer.rs @@ -1,69 +1,75 @@ -use wgpu_test::{fail, gpu_test, GpuTestConfiguration}; +use wgpu_test::{fail, gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(COPY_OVERFLOW_Z); +} #[gpu_test] -static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - let mut encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); +static COPY_OVERFLOW_Z: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); - let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size: wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }, - format: wgpu::TextureFormat::Rgba8Uint, - usage: wgpu::TextureUsages::COPY_DST, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); - let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - dimension: wgpu::TextureDimension::D2, - size: wgpu::Extent3d { - width: 256, - height: 256, - depth_or_array_layers: 1, - }, - format: wgpu::TextureFormat::Rgba8Uint, - usage: wgpu::TextureUsages::COPY_DST, - mip_level_count: 1, - sample_count: 1, - view_formats: &[], - }); + let t1 = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size: wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + format: wgpu::TextureFormat::Rgba8Uint, + usage: wgpu::TextureUsages::COPY_DST, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + let t2 = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size: wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + format: wgpu::TextureFormat::Rgba8Uint, + usage: wgpu::TextureUsages::COPY_DST, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); - fail( - &ctx.device, - || { - // Validation should catch the silly selected z layer range without panicking. - encoder.copy_texture_to_texture( - wgpu::TexelCopyTextureInfo { - texture: &t1, - mip_level: 1, - origin: wgpu::Origin3d::ZERO, - aspect: wgpu::TextureAspect::All, - }, - wgpu::TexelCopyTextureInfo { - texture: &t2, - mip_level: 1, - origin: wgpu::Origin3d { - x: 0, - y: 0, - z: 3824276442, + fail( + &ctx.device, + || { + // Validation should catch the silly selected z layer range without panicking. + encoder.copy_texture_to_texture( + wgpu::TexelCopyTextureInfo { + texture: &t1, + mip_level: 1, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, }, - aspect: wgpu::TextureAspect::All, - }, - wgpu::Extent3d { - width: 100, - height: 3, - depth_or_array_layers: 613286111, - }, - ); - ctx.queue.submit(Some(encoder.finish())); - }, - Some("unable to select texture mip level"), - ); -}); + wgpu::TexelCopyTextureInfo { + texture: &t2, + mip_level: 1, + origin: wgpu::Origin3d { + x: 0, + y: 0, + z: 3824276442, + }, + aspect: wgpu::TextureAspect::All, + }, + wgpu::Extent3d { + width: 100, + height: 3, + depth_or_array_layers: 613286111, + }, + ); + ctx.queue.submit(Some(encoder.finish())); + }, + Some("unable to select texture mip level"), + ); + }); diff --git a/tests/tests/wgpu-gpu/transition_resources.rs b/tests/tests/wgpu-gpu/transition_resources.rs index 5ae8f9a2725..f46e5c9bb84 100644 --- a/tests/tests/wgpu-gpu/transition_resources.rs +++ b/tests/tests/wgpu-gpu/transition_resources.rs @@ -1,35 +1,41 @@ -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(TRANSITION_RESOURCES); +} #[gpu_test] -static TRANSITION_RESOURCES: GpuTestConfiguration = GpuTestConfiguration::new().run_sync(|ctx| { - let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { - label: None, - size: wgpu::Extent3d { - width: 32, - height: 32, - depth_or_array_layers: 1, - }, - mip_level_count: 1, - sample_count: 1, - dimension: wgpu::TextureDimension::D2, - format: wgpu::TextureFormat::Rgba8Unorm, - usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, - view_formats: &[], - }); +static TRANSITION_RESOURCES: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_sync(|ctx| { + let texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 32, + height: 32, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); - let mut encoder = ctx - .device - .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); - encoder.transition_resources( - std::iter::empty(), - [wgpu::TextureTransition { - texture: &texture, - selector: None, - state: wgpu::TextureUses::COLOR_TARGET, - }] - .into_iter(), - ); + encoder.transition_resources( + std::iter::empty(), + [wgpu::TextureTransition { + texture: &texture, + selector: None, + state: wgpu::TextureUses::COLOR_TARGET, + }] + .into_iter(), + ); - ctx.queue.submit([encoder.finish()]); -}); + ctx.queue.submit([encoder.finish()]); + }); diff --git a/tests/tests/wgpu-gpu/vertex_formats/mod.rs b/tests/tests/wgpu-gpu/vertex_formats/mod.rs index f63bc2d695e..89f8db03d62 100644 --- a/tests/tests/wgpu-gpu/vertex_formats/mod.rs +++ b/tests/tests/wgpu-gpu/vertex_formats/mod.rs @@ -6,6 +6,10 @@ use wgpu::util::{BufferInitDescriptor, DeviceExt}; use wgpu_test::{gpu_test, FailureCase, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.extend([VERTEX_FORMATS_ALL, VERTEX_FORMATS_10_10_10_2]); +} + #[derive(Debug, Copy, Clone)] enum TestCase { UnormsAndSnorms, @@ -377,11 +381,15 @@ async fn vertex_formats_common(ctx: TestingContext, tests: &[Test<'_>]) { // See https://github.com/gfx-rs/wgpu/issues/4732 for why this is split between two submissions // with a hard wait in between. ctx.queue.submit([encoder1.finish()]); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); ctx.queue.submit([encoder2.finish()]); let slice = cpu_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data: Vec = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec(); let case_name = format!("Case {:?}", test.case); diff --git a/tests/tests/wgpu-gpu/vertex_indices/mod.rs b/tests/tests/wgpu-gpu/vertex_indices/mod.rs index 709380ff342..4fc63631f1e 100644 --- a/tests/tests/wgpu-gpu/vertex_indices/mod.rs +++ b/tests/tests/wgpu-gpu/vertex_indices/mod.rs @@ -11,6 +11,10 @@ use wgpu::util::{BufferInitDescriptor, DeviceExt, RenderEncoder}; use wgpu::RenderBundleDescriptor; use wgpu_test::{gpu_test, GpuTestConfiguration, TestParameters, TestingContext}; +pub fn all_tests(vec: &mut Vec) { + vec.push(VERTEX_INDICES); +} + /// Generic struct representing a draw call struct Draw { vertex: Range, @@ -436,11 +440,15 @@ async fn vertex_index_common(ctx: TestingContext) { // See https://github.com/gfx-rs/wgpu/issues/4732 for why this is split between two submissions // with a hard wait in between. ctx.queue.submit([encoder1.finish()]); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); ctx.queue.submit([encoder2.finish()]); let slice = cpu_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data: Vec = bytemuck::cast_slice(&slice.get_mapped_range()).to_vec(); let case_name = format!( diff --git a/tests/tests/wgpu-gpu/vertex_state.rs b/tests/tests/wgpu-gpu/vertex_state.rs new file mode 100644 index 00000000000..73cc0ecb78c --- /dev/null +++ b/tests/tests/wgpu-gpu/vertex_state.rs @@ -0,0 +1,195 @@ +use wgpu::{ + util::{BufferInitDescriptor, DeviceExt}, + vertex_attr_array, +}; +use wgpu_test::{ + gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters, TestingContext, +}; + +pub fn all_tests(vec: &mut Vec) { + vec.push(SET_ARRAY_STRIDE_TO_0); +} + +#[gpu_test] +static SET_ARRAY_STRIDE_TO_0: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().limits(wgpu::Limits::downlevel_defaults())) + .run_async(set_array_stride_to_0); + +/// Tests that draws using a vertex buffer with stride of 0 works correctly (especially on the +/// D3D12 backend; see commentary within). +async fn set_array_stride_to_0(ctx: TestingContext) { + let position_buffer_content: &[f32; 12] = &[ + // Triangle 1 + -1.0, -1.0, // Bottom left + 1.0, 1.0, // Top right + -1.0, 1.0, // Top left + // Triangle 2 + -1.0, -1.0, // Bottom left + 1.0, -1.0, // Bottom right + 1.0, 1.0, // Top right + ]; + let position_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice::(position_buffer_content), + usage: wgpu::BufferUsages::VERTEX, + }); + + let color_buffer_content: &[f32; 4] = &[1.0, 1.0, 1.0, 1.0]; + let color_buffer = ctx.device.create_buffer_init(&BufferInitDescriptor { + label: None, + contents: bytemuck::cast_slice::(color_buffer_content), + usage: wgpu::BufferUsages::VERTEX, + }); + + let shader_src = " + struct VertexOutput { + @builtin(position) position: vec4f, + @location(0) color: vec4f, + } + + @vertex + fn vs_main(@location(0) position: vec2f, @location(1) color: vec4f) -> VertexOutput { + return VertexOutput(vec4f(position, 0.0, 1.0), color); + } + + @fragment + fn fs_main(@location(0) color: vec4f) -> @location(0) vec4f { + return color; + } + "; + + let shader = ctx + .device + .create_shader_module(wgpu::ShaderModuleDescriptor { + label: None, + source: wgpu::ShaderSource::Wgsl(shader_src.into()), + }); + + let vbl = [ + wgpu::VertexBufferLayout { + array_stride: 8, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &vertex_attr_array![0 => Float32x2], + }, + wgpu::VertexBufferLayout { + array_stride: 0, + step_mode: wgpu::VertexStepMode::Vertex, + attributes: &vertex_attr_array![1 => Float32x4], + }, + ]; + let pipeline_desc = wgpu::RenderPipelineDescriptor { + label: None, + layout: None, + vertex: wgpu::VertexState { + buffers: &vbl, + module: &shader, + entry_point: Some("vs_main"), + compilation_options: Default::default(), + }, + primitive: wgpu::PrimitiveState::default(), + depth_stencil: None, + multisample: wgpu::MultisampleState::default(), + fragment: Some(wgpu::FragmentState { + module: &shader, + entry_point: Some("fs_main"), + compilation_options: Default::default(), + targets: &[Some(wgpu::ColorTargetState { + format: wgpu::TextureFormat::R8Unorm, + blend: None, + write_mask: wgpu::ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }; + let mut first_pipeline_desc = pipeline_desc.clone(); + let mut first_vbl = vbl.clone(); + first_vbl[1].array_stride = 16; + first_pipeline_desc.vertex.buffers = &first_vbl; + let pipeline = ctx.device.create_render_pipeline(&pipeline_desc); + let first_pipeline = ctx.device.create_render_pipeline(&first_pipeline_desc); + + let out_texture = ctx.device.create_texture(&wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::RENDER_ATTACHMENT | wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + let out_texture_view = out_texture.create_view(&wgpu::TextureViewDescriptor::default()); + + let readback_buffer = ctx.device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 256 * 256, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor::default()); + + { + let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor { + label: None, + color_attachments: &[Some(wgpu::RenderPassColorAttachment { + ops: wgpu::Operations::default(), + resolve_target: None, + view: &out_texture_view, + depth_slice: None, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + // The D3D12 backend used to not set the stride of vertex buffers if it was 0. + rpass.set_pipeline(&first_pipeline); // This call caused the D3D12 backend to set the stride for the 2nd vertex buffer to 16. + rpass.set_pipeline(&pipeline); // This call doesn't set the stride for the 2nd vertex buffer to 0. + rpass.set_vertex_buffer(0, position_buffer.slice(..)); + rpass.set_vertex_buffer(1, color_buffer.slice(..)); + rpass.draw(0..6, 0..1); // Causing this draw to be skipped since it would read OOB of the 2nd vertex buffer. + } + + encoder.copy_texture_to_buffer( + wgpu::TexelCopyTextureInfo { + texture: &out_texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + wgpu::TexelCopyBufferInfo { + buffer: &readback_buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(256), + rows_per_image: None, + }, + }, + wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + ); + + ctx.queue.submit([encoder.finish()]); + + let slice = readback_buffer.slice(..); + slice.map_async(wgpu::MapMode::Read, |_| ()); + + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); + + let data = slice.get_mapped_range(); + let succeeded = data.iter().all(|b| *b == u8::MAX); + assert!(succeeded); +} diff --git a/tests/tests/wgpu-gpu/write_texture.rs b/tests/tests/wgpu-gpu/write_texture.rs index 12349945a88..0f19075b451 100644 --- a/tests/tests/wgpu-gpu/write_texture.rs +++ b/tests/tests/wgpu-gpu/write_texture.rs @@ -1,6 +1,16 @@ //! Tests for texture copy -use wgpu_test::{gpu_test, GpuTestConfiguration}; +use wgpu::*; +use wgpu_test::{gpu_test, GpuTestConfiguration, GpuTestInitializer, TestParameters}; + +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + WRITE_TEXTURE_SUBSET_2D, + WRITE_TEXTURE_SUBSET_3D, + WRITE_TEXTURE_NO_OOB, + WRITE_TEXTURE_VIA_STAGING_BUFFER, + ]); +} #[gpu_test] static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration = @@ -84,7 +94,9 @@ static WRITE_TEXTURE_SUBSET_2D: GpuTestConfiguration = let slice = read_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data: Vec = slice.get_mapped_range().to_vec(); for byte in &data[..(size as usize * 2)] { @@ -177,7 +189,9 @@ static WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration = let slice = read_buffer.slice(..); slice.map_async(wgpu::MapMode::Read, |_| ()); - ctx.async_poll(wgpu::PollType::wait()).await.unwrap(); + ctx.async_poll(wgpu::PollType::wait_indefinitely()) + .await + .unwrap(); let data: Vec = slice.get_mapped_range().to_vec(); for byte in &data[..((size * size) as usize * 2)] { @@ -189,8 +203,9 @@ static WRITE_TEXTURE_SUBSET_3D: GpuTestConfiguration = }); #[gpu_test] -static WRITE_TEXTURE_NO_OOB: GpuTestConfiguration = - GpuTestConfiguration::new().run_async(|ctx| async move { +static WRITE_TEXTURE_NO_OOB: GpuTestConfiguration = GpuTestConfiguration::new() + .parameters(TestParameters::default().enable_noop()) + .run_async(|ctx| async move { let size = 256; let tex = ctx.device.create_texture(&wgpu::TextureDescriptor { @@ -228,3 +243,109 @@ static WRITE_TEXTURE_NO_OOB: GpuTestConfiguration = }, ); }); + +// Test a writeTexture operation that will use the staging buffer. +// If run with the address sanitizer, this serves as a regression +// test for https://github.com/gfx-rs/wgpu/pull/7893. +#[gpu_test] +static WRITE_TEXTURE_VIA_STAGING_BUFFER: GpuTestConfiguration = GpuTestConfiguration::new() + .run_async(|ctx| async move { + let width = 89; + let height = 17; + + let tex = ctx.device.create_texture(&TextureDescriptor { + label: None, + dimension: TextureDimension::D2, + size: Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + format: TextureFormat::R8Uint, + usage: TextureUsages::COPY_DST + | TextureUsages::COPY_SRC + | TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + + let write_width: u32 = 31; + let write_height: u32 = 5; + let write_bytes_per_row: u32 = 113; + let write_data = (0..(write_height - 1) * write_bytes_per_row + write_width) + .map(|b| (b % 256) as u8) + .collect::>(); + + ctx.queue.write_texture( + TexelCopyTextureInfo { + texture: &tex, + mip_level: 0, + origin: Origin3d::ZERO, + aspect: TextureAspect::All, + }, + &write_data, + TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(write_bytes_per_row), + rows_per_image: Some(19), + }, + Extent3d { + width: write_width, + height: write_height, + depth_or_array_layers: 1, + }, + ); + + ctx.queue.submit(None); + + let read_bytes_per_row = wgt::COPY_BYTES_PER_ROW_ALIGNMENT; + let read_buffer = ctx.device.create_buffer(&BufferDescriptor { + label: None, + size: (height * read_bytes_per_row) as u64, + usage: BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let mut encoder = ctx + .device + .create_command_encoder(&CommandEncoderDescriptor { label: None }); + + encoder.copy_texture_to_buffer( + TexelCopyTextureInfo { + texture: &tex, + mip_level: 0, + origin: Origin3d::ZERO, + aspect: TextureAspect::All, + }, + TexelCopyBufferInfo { + buffer: &read_buffer, + layout: TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(read_bytes_per_row), + rows_per_image: Some(height), + }, + }, + Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + + ctx.queue.submit(Some(encoder.finish())); + + let slice = read_buffer.slice(..); + slice.map_async(MapMode::Read, |_| ()); + ctx.async_poll(PollType::wait_indefinitely()).await.unwrap(); + let read_data: Vec = slice.get_mapped_range().to_vec(); + + for x in 0..write_width { + for y in 0..write_height { + assert_eq!( + read_data[(y * read_bytes_per_row + x) as usize], + write_data[(y * write_bytes_per_row + x) as usize] + ); + } + } + }); diff --git a/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs b/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs index fa3f70023a2..5d52b6e982a 100644 --- a/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs +++ b/tests/tests/wgpu-gpu/zero_init_texture_after_discard.rs @@ -1,9 +1,18 @@ use wgpu::*; use wgpu_test::{ - gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, TestParameters, - TestingContext, + gpu_test, image::ReadbackBuffers, FailureCase, GpuTestConfiguration, GpuTestInitializer, + TestParameters, TestingContext, }; +pub fn all_tests(vec: &mut Vec) { + vec.extend([ + DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT, + DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER, + DISCARDING_DEPTH_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_IN_SAME_ENCODER, + DISCARDING_EITHER_DEPTH_OR_STENCIL_ASPECT_TEST, + ]); +} + // Checks if discarding a color target resets its init state, causing a zero read of this texture when copied in after submit of the encoder. #[gpu_test] static DISCARDING_COLOR_TARGET_RESETS_TEXTURE_INIT_STATE_CHECK_VISIBLE_ON_COPY_AFTER_SUBMIT: diff --git a/tests/tests/wgpu-validation/api/buffer_mapping.rs b/tests/tests/wgpu-validation/api/buffer_mapping.rs new file mode 100644 index 00000000000..54e6bbdbd2c --- /dev/null +++ b/tests/tests/wgpu-validation/api/buffer_mapping.rs @@ -0,0 +1,191 @@ +fn mapping_is_zeroed(array: &[u8]) { + for (i, &byte) in array.iter().enumerate() { + assert_eq!(byte, 0, "Byte at index {i} is not zero"); + } +} + +// Ensure that a simple immutable mapping works and it is zeroed. +#[test] +fn full_immutable_binding() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + buffer.map_async(wgpu::MapMode::Read, .., |_| {}); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); + + let mapping = buffer.slice(..).get_mapped_range(); + + mapping_is_zeroed(&mapping); + + drop(mapping); + + buffer.unmap(); +} + +// Ensure that a simple mutable binding works and it is zeroed. +#[test] +fn full_mut_binding() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: true, + }); + + let mapping = buffer.slice(..).get_mapped_range_mut(); + + mapping_is_zeroed(&mapping); + + drop(mapping); + + buffer.unmap(); +} + +// Ensure that you can make two non-overlapping immutable ranges, which are both zeroed +#[test] +fn split_immutable_binding() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + + buffer.map_async(wgpu::MapMode::Read, .., |_| {}); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); + + let mapping0 = buffer.slice(0..512).get_mapped_range(); + let mapping1 = buffer.slice(512..1024).get_mapped_range(); + + mapping_is_zeroed(&mapping0); + mapping_is_zeroed(&mapping1); + + drop(mapping0); + drop(mapping1); + + buffer.unmap(); +} + +/// Ensure that you can make two non-overlapping mapped ranges, which are both zeroed +#[test] +fn split_mut_binding() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: true, + }); + + let mapping0 = buffer.slice(0..512).get_mapped_range_mut(); + let mapping1 = buffer.slice(512..1024).get_mapped_range_mut(); + + mapping_is_zeroed(&mapping0); + mapping_is_zeroed(&mapping1); + + drop(mapping0); + drop(mapping1); + + buffer.unmap(); +} + +/// Ensure that you can make two overlapping immutablely mapped ranges. +#[test] +fn overlapping_ref_binding() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: true, + }); + + let _mapping0 = buffer.slice(0..512).get_mapped_range(); + let _mapping1 = buffer.slice(256..768).get_mapped_range(); +} + +/// Ensure that two overlapping mutably mapped ranges panics. +#[test] +#[should_panic(expected = "break Rust memory aliasing rules")] +fn overlapping_mut_binding() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: true, + }); + + let _mapping0 = buffer.slice(0..512).get_mapped_range_mut(); + let _mapping1 = buffer.slice(256..768).get_mapped_range_mut(); +} + +/// Ensure that when you try to get a mapped range from an unmapped buffer, it panics with +/// an error mentioning a completely unmapped buffer. +#[test] +#[should_panic(expected = "an unmapped buffer")] +fn not_mapped() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + + let _mapping = buffer.slice(..).get_mapped_range_mut(); +} + +/// Ensure that when you partially map a buffer, then try to read outside of that range, it panics +/// mentioning the mapped indices. +#[test] +#[should_panic( + expected = "Attempted to get range 512..1024 (Mutable), but the mapped range is 0..512" +)] +fn partially_mapped() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + + buffer.map_async(wgpu::MapMode::Write, 0..512, |_| {}); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); + + let _mapping0 = buffer.slice(0..512).get_mapped_range_mut(); + let _mapping1 = buffer.slice(512..1024).get_mapped_range_mut(); +} + +/// Ensure that you cannot unmap a buffer while there are still accessible mapped views. +#[test] +#[should_panic(expected = "You cannot unmap a buffer that still has accessible mapped views")] +fn unmap_while_visible() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: 1024, + usage: wgpu::BufferUsages::MAP_WRITE | wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: true, + }); + + let _mapping0 = buffer.slice(..).get_mapped_range_mut(); + buffer.unmap(); +} diff --git a/tests/tests/wgpu-validation/api/command_buffer_actions.rs b/tests/tests/wgpu-validation/api/command_buffer_actions.rs new file mode 100644 index 00000000000..24b5ac255c8 --- /dev/null +++ b/tests/tests/wgpu-validation/api/command_buffer_actions.rs @@ -0,0 +1,261 @@ +use std::sync::atomic::{AtomicBool, AtomicU32, Ordering::SeqCst}; +use std::sync::Arc; + +/// Helper to create a small mappable buffer for READ tests. +fn make_read_buffer(device: &wgpu::Device, size: u64) -> wgpu::Buffer { + device.create_buffer(&wgpu::BufferDescriptor { + label: Some("read buffer"), + size, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }) +} + +/// map_buffer_on_submit defers mapping until submit, then invokes the callback after polling. +#[test] +fn encoder_map_buffer_on_submit_defers_until_submit() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 16); + + let fired = Arc::new(AtomicBool::new(false)); + let fired_cl = Arc::clone(&fired); + + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("encoder"), + }); + + // Register deferred map. + encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, move |_| { + fired_cl.store(true, SeqCst); + }); + // Include a trivial command that uses the buffer. + encoder.clear_buffer(&buffer, 0, None); + + // Polling before submit should not trigger the callback. + _ = device.poll(wgpu::PollType::Poll); + assert!(!fired.load(SeqCst)); + + // Submit and wait; callback should fire. + queue.submit([encoder.finish()]); + _ = device.poll(wgpu::PollType::wait_indefinitely()); + assert!(fired.load(SeqCst)); +} + +/// Empty ranges panic immediately when registering the deferred map. +#[test] +#[should_panic = "buffer slices can not be empty"] +fn encoder_map_buffer_on_submit_empty_range_panics_immediately() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 16); + + let encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + // This panics inside map_buffer_on_submit (range_to_offset_size). + encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 8..8, |_| {}); +} + +/// Out-of-bounds ranges panic during submit (when the deferred map executes). +#[test] +#[should_panic = "is out of range for buffer of size"] +fn encoder_map_buffer_on_submit_out_of_bounds_panics_on_submit() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 16); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + // 12..24 overflows the 16-byte buffer (size=12, end=24). + encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 12..24, |_| {}); + encoder.clear_buffer(&buffer, 0, None); + + // Panic happens inside submit when executing deferred actions. + queue.submit([encoder.finish()]); +} + +/// If the buffer is already mapped when the deferred mapping executes, it panics during submit. +#[test] +#[should_panic = "Buffer with 'read buffer' label is still mapped"] +fn encoder_map_buffer_on_submit_panics_if_already_mapped_on_submit() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 16); + + // Start a mapping now so the buffer is considered mapped. + buffer.slice(0..4).map_async(wgpu::MapMode::Read, |_| {}); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + // Deferred mapping of an already-mapped buffer will panic when executed on submit or be rejected by submit. + encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, |_| {}); + // Include any trivial work; using the same buffer ensures core validation catches the mapped hazard. + encoder.clear_buffer(&buffer, 0, None); + + queue.submit([encoder.finish()]); +} + +/// on_submitted_work_done is deferred until submit. +#[test] +fn encoder_on_submitted_work_done_defers_until_submit() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let fired = Arc::new(AtomicBool::new(false)); + let fired_cl = Arc::clone(&fired); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.on_submitted_work_done(move || { + fired_cl.store(true, SeqCst); + }); + + // Include a trivial command so the command buffer isn't completely empty. + let dummy = make_read_buffer(&device, 4); + encoder.clear_buffer(&dummy, 0, None); + + // Without submission, polling shouldn't invoke the callback. + _ = device.poll(wgpu::PollType::Poll); + assert!(!fired.load(SeqCst)); + + queue.submit([encoder.finish()]); + _ = device.poll(wgpu::PollType::wait_indefinitely()); + assert!(fired.load(SeqCst)); +} + +/// Both kinds of deferred callbacks are enqueued and eventually invoked. +#[test] +fn encoder_both_callbacks_fire_after_submit() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 16); + + let map_fired = Arc::new(AtomicBool::new(false)); + let map_fired_cl = Arc::clone(&map_fired); + let queue_fired = Arc::new(AtomicBool::new(false)); + let queue_fired_cl = Arc::clone(&queue_fired); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, move |_| { + map_fired_cl.store(true, SeqCst); + }); + encoder.on_submitted_work_done(move || { + queue_fired_cl.store(true, SeqCst); + }); + encoder.clear_buffer(&buffer, 0, None); + + queue.submit([encoder.finish()]); + _ = device.poll(wgpu::PollType::wait_indefinitely()); + + assert!(map_fired.load(SeqCst)); + assert!(queue_fired.load(SeqCst)); +} + +/// Registering multiple deferred mappings works; all callbacks fire after submit. +#[test] +fn encoder_multiple_map_buffer_on_submit_callbacks_fire() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer1 = make_read_buffer(&device, 32); + let buffer2 = make_read_buffer(&device, 32); + + let counter = Arc::new(AtomicU32::new(0)); + let c1 = Arc::clone(&counter); + let c2 = Arc::clone(&counter); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + encoder.map_buffer_on_submit(&buffer1, wgpu::MapMode::Read, 0..4, move |_| { + c1.fetch_add(1, SeqCst); + }); + encoder.map_buffer_on_submit(&buffer2, wgpu::MapMode::Read, 8..12, move |_| { + c2.fetch_add(1, SeqCst); + }); + encoder.clear_buffer(&buffer1, 0, None); + + queue.submit([encoder.finish()]); + _ = device.poll(wgpu::PollType::wait_indefinitely()); + + assert_eq!(counter.load(SeqCst), 2); +} + +/// Mapping with a buffer lacking MAP_* usage should panic when executed on submit. +#[test] +#[should_panic] +fn encoder_map_buffer_on_submit_panics_if_usage_invalid_on_submit() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let unmappable = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("unmappable buffer"), + size: 16, + usage: wgpu::BufferUsages::COPY_DST, // No MAP_READ or MAP_WRITE + mapped_at_creation: false, + }); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + encoder.map_buffer_on_submit(&unmappable, wgpu::MapMode::Read, 0..4, |_| {}); + + // Add unrelated work so the submission isn't empty. + let dummy = make_read_buffer(&device, 4); + encoder.clear_buffer(&dummy, 0, None); + + // Panic expected when deferred mapping executes. + queue.submit([encoder.finish()]); +} + +/// Deferred map callbacks run before on_submitted_work_done for the same submission. +#[test] +fn encoder_deferred_map_runs_before_on_submitted_work_done() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 16); + + #[derive(Default)] + struct Order { + map_order: AtomicU32, + queue_order: AtomicU32, + counter: AtomicU32, + } + let order = Arc::new(Order::default()); + let o_map = Arc::clone(&order); + let o_queue = Arc::clone(&order); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + encoder.map_buffer_on_submit(&buffer, wgpu::MapMode::Read, 0..4, move |_| { + let v = o_map.counter.fetch_add(1, SeqCst); + o_map.map_order.store(v, SeqCst); + }); + encoder.on_submitted_work_done(move || { + let v = o_queue.counter.fetch_add(1, SeqCst); + o_queue.queue_order.store(v, SeqCst); + }); + encoder.clear_buffer(&buffer, 0, None); + + queue.submit([encoder.finish()]); + _ = device.poll(wgpu::PollType::wait_indefinitely()); + + assert_eq!(order.counter.load(SeqCst), 2); + assert_eq!(order.map_order.load(SeqCst), 0); + assert_eq!(order.queue_order.load(SeqCst), 1); +} + +/// Multiple on_submitted_work_done callbacks registered on encoder all fire after submit. +#[test] +fn encoder_multiple_on_submitted_callbacks_fire() { + let (device, queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let buffer = make_read_buffer(&device, 4); + + let counter = Arc::new(AtomicU32::new(0)); + let c1 = Arc::clone(&counter); + let c2 = Arc::clone(&counter); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + encoder.on_submitted_work_done(move || { + c1.fetch_add(1, SeqCst); + }); + encoder.on_submitted_work_done(move || { + c2.fetch_add(1, SeqCst); + }); + encoder.clear_buffer(&buffer, 0, None); + + queue.submit([encoder.finish()]); + _ = device.poll(wgpu::PollType::wait_indefinitely()); + + assert_eq!(counter.load(SeqCst), 2); +} diff --git a/tests/tests/wgpu-validation/api/device.rs b/tests/tests/wgpu-validation/api/device.rs new file mode 100644 index 00000000000..aec013db767 --- /dev/null +++ b/tests/tests/wgpu-validation/api/device.rs @@ -0,0 +1,44 @@ +/// Test that if another error occurs reentrantly in the [`wgpu::Device::on_uncaptured_error`] +/// handler, this does not result in a deadlock (as a previous implementation would have had). +#[cfg(not(target_family = "wasm"))] // test needs wgpu::Device: Send + Sync to achieve reentrance +#[test] +fn recursive_uncaptured_error() { + use std::sync::atomic::{AtomicU32, Ordering::Relaxed}; + use std::sync::Arc; + + const ERRONEOUS_TEXTURE_DESC: wgpu::TextureDescriptor = wgpu::TextureDescriptor { + label: None, + size: wgpu::Extent3d { + width: 1, + height: 1, + depth_or_array_layers: 10, + }, + mip_level_count: 0, + sample_count: 0, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8UnormSrgb, + usage: wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }; + + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let errors_seen: Arc = Arc::default(); + let handler = Arc::new({ + let errors_seen = errors_seen.clone(); + let device = device.clone(); + move |_error| { + let previous_count = errors_seen.fetch_add(1, Relaxed); + if previous_count == 0 { + // Trigger another error recursively + _ = device.create_texture(&ERRONEOUS_TEXTURE_DESC); + } + } + }); + + // Trigger one error which will call the handler + device.on_uncaptured_error(handler); + _ = device.create_texture(&ERRONEOUS_TEXTURE_DESC); + + assert_eq!(errors_seen.load(Relaxed), 2); +} diff --git a/tests/tests/wgpu-validation/api/experimental.rs b/tests/tests/wgpu-validation/api/experimental.rs new file mode 100644 index 00000000000..23cdc8ea4d3 --- /dev/null +++ b/tests/tests/wgpu-validation/api/experimental.rs @@ -0,0 +1,70 @@ +fn noop_adapter() -> wgpu::Adapter { + let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { + backends: wgpu::Backends::NOOP, + backend_options: wgpu::BackendOptions { + noop: wgpu::NoopBackendOptions { enable: true }, + ..Default::default() + }, + ..Default::default() + }); + + pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default())) + .expect("noop backend adapter absent when it should be") +} + +#[test] +fn request_no_experimental_features() { + let adapter = noop_adapter(); + + let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + // Not experimental + required_features: wgpu::Features::FLOAT32_FILTERABLE, + experimental_features: wgpu::ExperimentalFeatures::disabled(), + ..Default::default() + })); + + assert!(dq.is_ok()); +} + +#[test] +fn request_experimental_features() { + let adapter = noop_adapter(); + + let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + // Experimental + required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER, + experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }, + ..Default::default() + })); + + assert!(dq.is_ok()); +} + +#[test] +fn request_experimental_features_when_not_enabled() { + let adapter = noop_adapter(); + + let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + // Experimental + required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER, + experimental_features: wgpu::ExperimentalFeatures::disabled(), + ..Default::default() + })); + + assert!(dq.is_err()); +} + +#[test] +fn request_multiple_experimental_features_when_not_enabled() { + let adapter = noop_adapter(); + + let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + // Experimental + required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER + | wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS, + experimental_features: wgpu::ExperimentalFeatures::disabled(), + ..Default::default() + })); + + assert!(dq.is_err()); +} diff --git a/tests/tests/wgpu-validation/api/external_texture.rs b/tests/tests/wgpu-validation/api/external_texture.rs index ece0e060f4f..989fe5ac8ae 100644 --- a/tests/tests/wgpu-validation/api/external_texture.rs +++ b/tests/tests/wgpu-validation/api/external_texture.rs @@ -1,6 +1,407 @@ use wgpu::*; use wgpu_test::{fail, valid}; +/// Ensures an [`ExternalTexture`] can be created from a valid descriptor and planes, +/// but appropriate errors are returned for invalid descriptors and planes. +#[test] +fn create_external_texture() { + let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor { + required_features: Features::EXTERNAL_TEXTURE, + ..Default::default() + }); + + let texture_descriptor = TextureDescriptor { + label: None, + size: Extent3d { + width: 512, + height: 512, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba8Unorm, + usage: TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }; + + let r_texture = device.create_texture(&TextureDescriptor { + format: TextureFormat::R8Unorm, + ..texture_descriptor + }); + let r_view = r_texture.create_view(&TextureViewDescriptor::default()); + let rg_texture = device.create_texture(&TextureDescriptor { + format: TextureFormat::Rg8Unorm, + ..texture_descriptor + }); + let rg_view = rg_texture.create_view(&TextureViewDescriptor::default()); + let rgba_texture = device.create_texture(&TextureDescriptor { + format: TextureFormat::Rgba8Unorm, + ..texture_descriptor + }); + let rgba_view = rgba_texture.create_view(&TextureViewDescriptor::default()); + + let _ = valid(&device, || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&rgba_view], + ) + }); + let _ = valid(&device, || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Nv12, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view, &rg_view], + ) + }); + let _ = valid(&device, || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Yu12, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view, &r_view, &r_view], + ) + }); + + // Wrong number of planes for format + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view, &r_view], + ) + }, + Some("External texture format Rgba expects 1 planes, but given 2"), + ); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Nv12, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view], + ) + }, + Some("External texture format Nv12 expects 2 planes, but given 1"), + ); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Yu12, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view, &r_view], + ) + }, + Some("External texture format Yu12 expects 3 planes, but given 2"), + ); + + // Wrong plane formats + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view], + ) + }, + Some("External texture format Rgba plane 0 expects format with 4 components but given view with format R8Unorm (1 components)"), + ); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Nv12, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view, &rgba_view], + ) + }, + Some("External texture format Nv12 plane 1 expects format with 2 components but given view with format Rgba8Unorm (4 components)"), + ); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Yu12, + label: None, + width: r_texture.width(), + height: r_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&r_view, &rg_view, &r_view], + ) + }, + Some("External texture format Yu12 plane 1 expects format with 1 components but given view with format Rg8Unorm (2 components)"), + ); + + // Wrong sample type + let uint_texture = device.create_texture(&TextureDescriptor { + format: TextureFormat::Rgba8Uint, + ..texture_descriptor + }); + let uint_view = uint_texture.create_view(&TextureViewDescriptor::default()); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: uint_texture.width(), + height: uint_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&uint_view], + ) + }, + Some("External texture planes expect a filterable float sample type, but given view with format Rgba8Uint (sample type Uint)"), + ); + + // Wrong texture dimension + let d3_texture = device.create_texture(&TextureDescriptor { + dimension: TextureDimension::D3, + ..texture_descriptor + }); + let d3_view = d3_texture.create_view(&TextureViewDescriptor::default()); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: d3_texture.width(), + height: d3_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&d3_view], + ) + }, + Some("External texture planes expect 2D dimension, but given view with dimension = D3"), + ); + + // Multisampled + let multisampled_texture = device.create_texture(&TextureDescriptor { + sample_count: 4, + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + ..texture_descriptor + }); + let multisampled_view = multisampled_texture.create_view(&TextureViewDescriptor::default()); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: multisampled_texture.width(), + height: multisampled_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&multisampled_view], + ) + }, + Some("External texture planes cannot be multisampled, but given view with samples = 4"), + ); + + // Missing TEXTURE_BINDING + let non_binding_texture = device.create_texture(&TextureDescriptor { + usage: TextureUsages::STORAGE_BINDING, + ..texture_descriptor + }); + let non_binding_view = non_binding_texture.create_view(&TextureViewDescriptor::default()); + let _ = fail( + &device, + || { + device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: non_binding_texture.width(), + height: non_binding_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&non_binding_view], + ) + }, + Some("Usage flags TextureUsages(STORAGE_BINDING) of TextureView with '' label do not contain required usage flags TextureUsages(TEXTURE_BINDING)"), + ); +} + +/// Ensures an [`ExternalTexture`] can be bound to a [`BindingType::ExternalTexture`] +/// resource binding. +#[test] +fn external_texture_binding() { + let (device, _queue) = wgpu::Device::noop(&DeviceDescriptor { + required_features: Features::EXTERNAL_TEXTURE, + ..Default::default() + }); + + let bgl = valid(&device, || { + device.create_bind_group_layout(&BindGroupLayoutDescriptor { + label: None, + entries: &[BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::ExternalTexture, + count: None, + }], + }) + }); + + let texture_descriptor = TextureDescriptor { + label: None, + size: Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba8Unorm, + usage: TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }; + let external_texture_descriptor = ExternalTextureDescriptor { + label: None, + width: texture_descriptor.size.width, + height: texture_descriptor.size.height, + format: ExternalTextureFormat::Rgba, + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }; + + valid(&device, || { + let texture = device.create_texture(&texture_descriptor); + let view = texture.create_view(&TextureViewDescriptor::default()); + let external_texture = + device.create_external_texture(&external_texture_descriptor, &[&view]); + + device.create_bind_group(&BindGroupDescriptor { + label: None, + layout: &bgl, + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::ExternalTexture(&external_texture), + }], + }) + }); +} + /// Ensures a [`TextureView`] can be bound to a [`BindingType::ExternalTexture`] /// resource binding. #[test] @@ -160,3 +561,142 @@ fn external_texture_binding_texture_view() { Some("Texture binding 0 expects multisampled = false, but given a view with samples = 4"), ); } + +/// Ensures that submitting a command buffer referencing an external texture, any of +/// whose plane textures have already been destroyed, results in an error. +#[test] +fn destroyed_external_texture_plane() { + let (device, queue) = wgpu::Device::noop(&DeviceDescriptor { + required_features: Features::EXTERNAL_TEXTURE, + ..Default::default() + }); + + let target_texture = device.create_texture(&TextureDescriptor { + label: None, + size: Extent3d { + width: 512, + height: 512, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba8Unorm, + usage: TextureUsages::RENDER_ATTACHMENT, + view_formats: &[], + }); + let target_view = target_texture.create_view(&TextureViewDescriptor::default()); + + let plane_texture = device.create_texture(&TextureDescriptor { + label: Some("External texture plane"), + size: Extent3d { + width: 512, + height: 512, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: TextureFormat::Rgba8Unorm, + usage: TextureUsages::TEXTURE_BINDING, + view_formats: &[], + }); + let plane_view = plane_texture.create_view(&TextureViewDescriptor::default()); + + let external_texture = device.create_external_texture( + &ExternalTextureDescriptor { + format: ExternalTextureFormat::Rgba, + label: None, + width: plane_texture.width(), + height: plane_texture.height(), + yuv_conversion_matrix: [0.0; 16], + gamut_conversion_matrix: [0.0; 9], + src_transfer_function: Default::default(), + dst_transfer_function: Default::default(), + sample_transform: [0.0; 6], + load_transform: [0.0; 6], + }, + &[&plane_view], + ); + + let module = device.create_shader_module(ShaderModuleDescriptor { + label: None, + source: ShaderSource::Wgsl(std::borrow::Cow::Borrowed( + " +@group(0) @binding(0) +var tex: texture_external; +@vertex fn vert_main() -> @builtin(position) vec4 { return vec4(0); } +@fragment fn frag_main() -> @location(0) vec4 { return textureLoad(tex, vec2(0)); }", + )), + }); + + let pipeline = device.create_render_pipeline(&RenderPipelineDescriptor { + label: None, + layout: None, + vertex: VertexState { + module: &module, + entry_point: None, + compilation_options: PipelineCompilationOptions::default(), + buffers: &[], + }, + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + fragment: Some(FragmentState { + module: &module, + entry_point: None, + compilation_options: PipelineCompilationOptions::default(), + targets: &[Some(ColorTargetState { + format: target_texture.format(), + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + multiview: None, + cache: None, + }); + + let bind_group = device.create_bind_group(&BindGroupDescriptor { + label: None, + layout: &pipeline.get_bind_group_layout(0), + entries: &[BindGroupEntry { + binding: 0, + resource: BindingResource::ExternalTexture(&external_texture), + }], + }); + + let mut encoder = device.create_command_encoder(&CommandEncoderDescriptor { label: None }); + let mut pass = encoder.begin_render_pass(&RenderPassDescriptor { + label: None, + color_attachments: &[Some(RenderPassColorAttachment { + view: &target_view, + depth_slice: None, + resolve_target: None, + ops: Operations { + load: LoadOp::Clear(Color { + r: 0.0, + g: 0.0, + b: 0.0, + a: 1.0, + }), + store: StoreOp::Store, + }, + })], + depth_stencil_attachment: None, + timestamp_writes: None, + occlusion_query_set: None, + }); + + pass.set_pipeline(&pipeline); + pass.set_bind_group(0, &bind_group, &[]); + pass.draw(0..0, 0..0); + drop(pass); + + plane_texture.destroy(); + + fail( + &device, + || queue.submit([encoder.finish()]), + Some("Texture with 'External texture plane' label has been destroyed"), + ); +} diff --git a/tests/tests/wgpu-validation/api/instance.rs b/tests/tests/wgpu-validation/api/instance.rs index 4f30e90293f..5385b71a2b8 100644 --- a/tests/tests/wgpu-validation/api/instance.rs +++ b/tests/tests/wgpu-validation/api/instance.rs @@ -1,5 +1,5 @@ mod multi_instance { - #![cfg(not(target_arch = "wasm32"))] + #![cfg(not(any(target_arch = "wasm32", miri)))] async fn get() -> wgpu::Adapter { let adapter = { diff --git a/tests/tests/wgpu-validation/api/mod.rs b/tests/tests/wgpu-validation/api/mod.rs index 9ea34327b47..a9c6abf1d16 100644 --- a/tests/tests/wgpu-validation/api/mod.rs +++ b/tests/tests/wgpu-validation/api/mod.rs @@ -1,6 +1,10 @@ mod binding_arrays; mod buffer; +mod buffer_mapping; mod buffer_slice; +mod command_buffer_actions; +mod device; +mod experimental; mod external_texture; mod instance; mod texture; diff --git a/tests/tests/wgpu-validation/api/texture.rs b/tests/tests/wgpu-validation/api/texture.rs index 2b079cc3a78..01c88c7bdf7 100644 --- a/tests/tests/wgpu-validation/api/texture.rs +++ b/tests/tests/wgpu-validation/api/texture.rs @@ -1,5 +1,7 @@ //! Tests of [`wgpu::Texture`] and related. +use wgpu_test::{fail, valid}; + /// Ensures that submitting a command buffer referencing an already destroyed texture /// results in an error. #[test] @@ -54,3 +56,455 @@ fn destroyed_texture() { queue.submit([encoder.finish()]); } + +/// Ensures that creating a texture view from a specific plane of a planar +/// texture works as expected. +#[test] +fn planar_texture_view_plane() { + let required_features = wgpu::Features::TEXTURE_FORMAT_NV12 + | wgpu::Features::TEXTURE_FORMAT_P010 + | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM; + let device_desc = wgpu::DeviceDescriptor { + required_features, + ..Default::default() + }; + let (device, _queue) = wgpu::Device::noop(&device_desc); + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + + for (tex_format, view_format, view_aspect) in [ + ( + wgpu::TextureFormat::NV12, + wgpu::TextureFormat::R8Unorm, + wgpu::TextureAspect::Plane0, + ), + ( + wgpu::TextureFormat::NV12, + wgpu::TextureFormat::Rg8Unorm, + wgpu::TextureAspect::Plane1, + ), + ( + wgpu::TextureFormat::P010, + wgpu::TextureFormat::R16Unorm, + wgpu::TextureAspect::Plane0, + ), + ( + wgpu::TextureFormat::P010, + wgpu::TextureFormat::Rg16Unorm, + wgpu::TextureAspect::Plane1, + ), + ] { + let tex = device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: tex_format, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + valid(&device, || { + let _ = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(view_format), + aspect: view_aspect, + ..Default::default() + }); + }); + } +} + +/// Ensures that attempting to create a texture view from a specific plane of a +/// non-planar texture fails validation. +#[test] +fn non_planar_texture_view_plane() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + let tex = device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: wgpu::TextureFormat::R8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + fail( + &device, + || { + let _ = tex.create_view(&wgpu::TextureViewDescriptor { + aspect: wgpu::TextureAspect::Plane0, + ..Default::default() + }); + }, + Some("Aspect Plane0 is not in the source texture format R8Unorm"), + ); +} + +/// Ensures that attempting to create a texture view from an invalid plane of a +/// planar texture fails validation. +#[test] +fn planar_texture_view_plane_out_of_bounds() { + let required_features = wgpu::Features::TEXTURE_FORMAT_NV12 + | wgpu::Features::TEXTURE_FORMAT_P010 + | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM; + let device_desc = wgpu::DeviceDescriptor { + required_features, + ..Default::default() + }; + let (device, _queue) = wgpu::Device::noop(&device_desc); + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + + for (tex_format, view_format, view_aspect) in [ + ( + wgpu::TextureFormat::NV12, + wgpu::TextureFormat::R8Unorm, + wgpu::TextureAspect::Plane2, + ), + ( + wgpu::TextureFormat::P010, + wgpu::TextureFormat::R16Unorm, + wgpu::TextureAspect::Plane2, + ), + ] { + let tex = device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: tex_format, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + fail( + &device, + || { + let _ = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(view_format), + aspect: view_aspect, + ..Default::default() + }); + }, + Some(&format!( + "Aspect {view_aspect:?} is not in the source texture format {tex_format:?}" + )), + ); + } +} + +/// Ensures that attempting to create a texture view from a specific plane of a +/// planar texture with an invalid format fails validation. +#[test] +fn planar_texture_view_plane_bad_format() { + let required_features = wgpu::Features::TEXTURE_FORMAT_NV12 + | wgpu::Features::TEXTURE_FORMAT_P010 + | wgpu::Features::TEXTURE_FORMAT_16BIT_NORM; + let device_desc = wgpu::DeviceDescriptor { + required_features, + ..Default::default() + }; + let (device, _queue) = wgpu::Device::noop(&device_desc); + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + for (tex_format, view_format, view_aspect) in [ + ( + wgpu::TextureFormat::NV12, + wgpu::TextureFormat::Rg8Unorm, + wgpu::TextureAspect::Plane0, + ), + ( + wgpu::TextureFormat::P010, + wgpu::TextureFormat::Rg16Unorm, + wgpu::TextureAspect::Plane0, + ), + ] { + let tex = device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format: tex_format, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + fail( + &device, + || { + let _ = tex.create_view(&wgpu::TextureViewDescriptor { + format: Some(view_format), + aspect: view_aspect, + ..Default::default() + }); + }, + Some(&format!( + "unable to view texture {tex_format:?} as {view_format:?}" + )), + ); + } +} + +/// Ensures that attempting to create a planar texture with an invalid size +/// fails validation. +#[test] +fn planar_texture_bad_size() { + let required_features = + wgpu::Features::TEXTURE_FORMAT_NV12 | wgpu::Features::TEXTURE_FORMAT_P010; + let device_desc = wgpu::DeviceDescriptor { + required_features, + ..Default::default() + }; + let (device, _queue) = wgpu::Device::noop(&device_desc); + let size = wgpu::Extent3d { + width: 255, + height: 255, + depth_or_array_layers: 1, + }; + for format in [wgpu::TextureFormat::NV12, wgpu::TextureFormat::P010] { + fail( + &device, + || { + let _ = device.create_texture(&wgpu::TextureDescriptor { + label: None, + dimension: wgpu::TextureDimension::D2, + size, + format, + usage: wgpu::TextureUsages::TEXTURE_BINDING, + mip_level_count: 1, + sample_count: 1, + view_formats: &[], + }); + }, + Some(&format!( + "width {} is not a multiple of {format:?}'s width multiple requirement", + size.width + )), + ); + } +} + +/// Creates a texture and a buffer, and encodes a copy from the texture to the +/// buffer. +fn encode_copy_texture_to_buffer( + device: &wgpu::Device, + format: wgpu::TextureFormat, + aspect: wgpu::TextureAspect, + bytes_per_texel: u32, +) -> wgpu::CommandEncoder { + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::COPY_SRC, + view_formats: &[], + }); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: (size.width * size.height * bytes_per_texel) as u64, + usage: wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.copy_texture_to_buffer( + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect, + }, + wgpu::TexelCopyBufferInfo { + buffer: &buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(size.width * bytes_per_texel), + rows_per_image: None, + }, + }, + size, + ); + + encoder +} + +/// Ensures that attempting to copy a texture with a forbidden source format to +/// a buffer fails validation. +#[test] +fn copy_texture_to_buffer_forbidden_format() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let format = wgpu::TextureFormat::Depth24Plus; + + let encoder = encode_copy_texture_to_buffer(&device, format, wgpu::TextureAspect::All, 4); + + fail( + &device, + || { + encoder.finish(); + }, + Some(&format!( + "Copying from textures with format {format:?} is forbidden" + )), + ); +} + +/// Ensures that attempting ta copy a texture with a forbidden source +/// format/aspect combination to a buffer fails validation. +#[test] +fn copy_texture_to_buffer_forbidden_format_aspect() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + let format = wgpu::TextureFormat::Depth24PlusStencil8; + let aspect = wgpu::TextureAspect::DepthOnly; + + let encoder = encode_copy_texture_to_buffer(&device, format, aspect, 4); + + fail( + &device, + || { + encoder.finish(); + }, + Some(&format!( + "Copying from textures with format {format:?} and aspect {aspect:?} is forbidden" + )), + ); +} + +/// Creates a texture and a buffer, and encodes a copy from the buffer to the +/// texture. +fn encode_copy_buffer_to_texture( + device: &wgpu::Device, + format: wgpu::TextureFormat, + aspect: wgpu::TextureAspect, + bytes_per_texel: u32, +) -> wgpu::CommandEncoder { + let size = wgpu::Extent3d { + width: 256, + height: 256, + depth_or_array_layers: 1, + }; + + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: None, + size, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format, + usage: wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: None, + size: (size.width * size.height * bytes_per_texel) as u64, + usage: wgpu::BufferUsages::COPY_SRC, + mapped_at_creation: false, + }); + + let mut encoder = + device.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + + encoder.copy_buffer_to_texture( + wgpu::TexelCopyBufferInfo { + buffer: &buffer, + layout: wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(size.width * bytes_per_texel), + rows_per_image: None, + }, + }, + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect, + }, + size, + ); + + encoder +} + +/// Ensures that attempting to copy a buffer to a texture with a forbidden +/// destination format fails validation. +#[test] +fn copy_buffer_to_texture_forbidden_format() { + let (device, _queue) = wgpu::Device::noop(&wgpu::DeviceDescriptor::default()); + + for format in [ + wgpu::TextureFormat::Depth24Plus, + wgpu::TextureFormat::Depth32Float, + ] { + let encoder = encode_copy_buffer_to_texture(&device, format, wgpu::TextureAspect::All, 4); + + fail( + &device, + || { + encoder.finish(); + }, + Some(&format!( + "Copying to textures with format {format:?} is forbidden" + )), + ); + } +} + +/// Ensures that attempting to copy a buffer to a texture with a forbidden +/// destination format/aspect combination fails validation. +#[test] +fn copy_buffer_to_texture_forbidden_format_aspect() { + let required_features = wgpu::Features::DEPTH32FLOAT_STENCIL8; + let device_desc = wgpu::DeviceDescriptor { + required_features, + ..Default::default() + }; + let (device, _queue) = wgpu::Device::noop(&device_desc); + + let aspect = wgpu::TextureAspect::DepthOnly; + + for (format, bytes_per_texel) in [ + (wgpu::TextureFormat::Depth24PlusStencil8, 4), + (wgpu::TextureFormat::Depth32FloatStencil8, 8), + ] { + let encoder = encode_copy_buffer_to_texture(&device, format, aspect, bytes_per_texel); + + fail( + &device, + || { + encoder.finish(); + }, + Some(&format!( + "Copying to textures with format {format:?} and aspect {aspect:?} is forbidden" + )), + ); + } +} diff --git a/tests/tests/wgpu-validation/noop.rs b/tests/tests/wgpu-validation/noop.rs index 76bfff48155..bf5fcbbd035 100644 --- a/tests/tests/wgpu-validation/noop.rs +++ b/tests/tests/wgpu-validation/noop.rs @@ -53,6 +53,6 @@ fn device_and_buffers() { assert_eq!(*result.unwrap(), [1, 2, 3, 4, 5, 6, 7, 8],); done.store(true, Relaxed); }); - device.poll(wgpu::PollType::Wait).unwrap(); + device.poll(wgpu::PollType::wait_indefinitely()).unwrap(); assert!(done2.load(Relaxed)); } diff --git a/typos.toml b/typos.toml index d93e1f8ecdc..f5efa9df29f 100644 --- a/typos.toml +++ b/typos.toml @@ -20,6 +20,9 @@ extend-exclude = [ lod = "lod" metalness = "metalness" +# A DXC command line argument +Fo = "Fo" + # Usernames Healthire = "Healthire" REASY = "REASY" diff --git a/wgpu-core/Cargo.toml b/wgpu-core/Cargo.toml index ee131dc9f82..170d0f74928 100644 --- a/wgpu-core/Cargo.toml +++ b/wgpu-core/Cargo.toml @@ -72,14 +72,7 @@ observe_locks = ["std", "dep:ron", "serde/serde_derive"] serde = ["dep:serde", "wgpu-types/serde", "arrayvec/serde", "hashbrown/serde"] ## Enable API tracing. -trace = [ - "serde", - "std", - "dep:ron", - "naga/serialize", - "wgpu-types/trace", - "dep:bytemuck", -] +trace = ["serde", "std", "dep:ron", "naga/serialize", "wgpu-types/trace"] ## Enable API replaying replay = ["serde", "naga/deserialize"] @@ -100,7 +93,7 @@ wgsl = ["naga/wgsl-in"] glsl = ["naga/glsl-in"] ## Enable `ShaderModuleSource::SpirV` -spirv = ["naga/spv-in", "dep:bytemuck"] +spirv = ["naga/spv-in"] #! ### Other # -------------------------------------------------------------------- @@ -168,7 +161,7 @@ wgpu-core-deps-apple = { workspace = true, optional = true } wgpu-core-deps-emscripten = { workspace = true, optional = true } [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] wgpu-core-deps-wasm = { workspace = true, optional = true } -[target.'cfg(any(windows, target_os = "linux", target_os = "android"))'.dependencies] +[target.'cfg(any(windows, target_os = "linux", target_os = "android", target_os = "freebsd"))'.dependencies] wgpu-core-deps-windows-linux-android = { workspace = true, optional = true } [dependencies] @@ -180,7 +173,7 @@ arrayvec.workspace = true bit-vec.workspace = true bit-set.workspace = true bitflags.workspace = true -bytemuck = { workspace = true, optional = true } +bytemuck.workspace = true document-features.workspace = true hashbrown.workspace = true indexmap.workspace = true diff --git a/wgpu-core/LICENSE.APACHE b/wgpu-core/LICENSE.APACHE deleted file mode 120000 index 7141cad5b28..00000000000 --- a/wgpu-core/LICENSE.APACHE +++ /dev/null @@ -1 +0,0 @@ -../LICENSE.APACHE \ No newline at end of file diff --git a/wgpu-core/LICENSE.APACHE b/wgpu-core/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/wgpu-core/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/wgpu-core/LICENSE.MIT b/wgpu-core/LICENSE.MIT deleted file mode 120000 index 6b8772d1a7a..00000000000 --- a/wgpu-core/LICENSE.MIT +++ /dev/null @@ -1 +0,0 @@ -../LICENSE.MIT \ No newline at end of file diff --git a/wgpu-core/LICENSE.MIT b/wgpu-core/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/wgpu-core/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/wgpu-core/build.rs b/wgpu-core/build.rs index a01f5afe3bc..15982414032 100644 --- a/wgpu-core/build.rs +++ b/wgpu-core/build.rs @@ -1,6 +1,6 @@ fn main() { cfg_aliases::cfg_aliases! { - windows_linux_android: { any(windows, target_os = "linux", target_os = "android") }, + windows_linux_android: { any(windows, target_os = "linux", target_os = "android", target_os = "freebsd") }, send_sync: { all( feature = "std", any( diff --git a/wgpu-core/platform-deps/apple/LICENSE.APACHE b/wgpu-core/platform-deps/apple/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/wgpu-core/platform-deps/apple/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/wgpu-core/platform-deps/apple/LICENSE.MIT b/wgpu-core/platform-deps/apple/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/wgpu-core/platform-deps/apple/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/wgpu-core/platform-deps/emscripten/LICENSE.APACHE b/wgpu-core/platform-deps/emscripten/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/wgpu-core/platform-deps/emscripten/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/wgpu-core/platform-deps/emscripten/LICENSE.MIT b/wgpu-core/platform-deps/emscripten/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/wgpu-core/platform-deps/emscripten/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/wgpu-core/platform-deps/wasm/LICENSE.APACHE b/wgpu-core/platform-deps/wasm/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/wgpu-core/platform-deps/wasm/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/wgpu-core/platform-deps/wasm/LICENSE.MIT b/wgpu-core/platform-deps/wasm/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/wgpu-core/platform-deps/wasm/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/wgpu-core/platform-deps/windows-linux-android/Cargo.toml b/wgpu-core/platform-deps/windows-linux-android/Cargo.toml index 7de228cdfb4..c014b8feba8 100644 --- a/wgpu-core/platform-deps/windows-linux-android/Cargo.toml +++ b/wgpu-core/platform-deps/windows-linux-android/Cargo.toml @@ -23,5 +23,5 @@ dx12 = ["wgpu-hal/dx12"] renderdoc = ["wgpu-hal/renderdoc"] # Depend on wgpu-hal conditionally, so that the above features only apply to wgpu-hal on this set of platforms. -[target.'cfg(any(windows, target_os = "linux", target_os = "android"))'.dependencies] +[target.'cfg(any(windows, target_os = "linux", target_os = "android", target_os = "freebsd"))'.dependencies] wgpu-hal.workspace = true diff --git a/wgpu-core/platform-deps/windows-linux-android/LICENSE.APACHE b/wgpu-core/platform-deps/windows-linux-android/LICENSE.APACHE new file mode 100644 index 00000000000..d9a10c0d8e8 --- /dev/null +++ b/wgpu-core/platform-deps/windows-linux-android/LICENSE.APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/wgpu-core/platform-deps/windows-linux-android/LICENSE.MIT b/wgpu-core/platform-deps/windows-linux-android/LICENSE.MIT new file mode 100644 index 00000000000..8d02e4dbd5f --- /dev/null +++ b/wgpu-core/platform-deps/windows-linux-android/LICENSE.MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 The gfx-rs developers + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/wgpu-core/src/as_hal.rs b/wgpu-core/src/as_hal.rs index 1191bcbbd62..390c496236e 100644 --- a/wgpu-core/src/as_hal.rs +++ b/wgpu-core/src/as_hal.rs @@ -6,7 +6,6 @@ use hal::DynResource; use crate::{ device::Device, global::Global, - hal_api::HalApi, id::{ AdapterId, BlasId, BufferId, CommandEncoderId, DeviceId, QueueId, SurfaceId, TextureId, TextureViewId, TlasId, @@ -226,7 +225,7 @@ impl Global { /// # Safety /// /// - The raw buffer handle must not be manually destroyed - pub unsafe fn buffer_as_hal( + pub unsafe fn buffer_as_hal( &self, id: BufferId, ) -> Option> { @@ -242,7 +241,7 @@ impl Global { /// # Safety /// /// - The raw texture handle must not be manually destroyed - pub unsafe fn texture_as_hal( + pub unsafe fn texture_as_hal( &self, id: TextureId, ) -> Option> { @@ -258,7 +257,7 @@ impl Global { /// # Safety /// /// - The raw texture view handle must not be manually destroyed - pub unsafe fn texture_view_as_hal( + pub unsafe fn texture_view_as_hal( &self, id: TextureViewId, ) -> Option> { @@ -274,7 +273,7 @@ impl Global { /// # Safety /// /// - The raw adapter handle must not be manually destroyed - pub unsafe fn adapter_as_hal( + pub unsafe fn adapter_as_hal( &self, id: AdapterId, ) -> Option> { @@ -291,7 +290,7 @@ impl Global { /// # Safety /// /// - The raw device handle must not be manually destroyed - pub unsafe fn device_as_hal( + pub unsafe fn device_as_hal( &self, id: DeviceId, ) -> Option> { @@ -305,7 +304,7 @@ impl Global { /// # Safety /// /// - The raw fence handle must not be manually destroyed - pub unsafe fn device_fence_as_hal( + pub unsafe fn device_fence_as_hal( &self, id: DeviceId, ) -> Option> { @@ -318,7 +317,7 @@ impl Global { /// # Safety /// - The raw surface handle must not be manually destroyed - pub unsafe fn surface_as_hal( + pub unsafe fn surface_as_hal( &self, id: SurfaceId, ) -> Option> { @@ -335,7 +334,7 @@ impl Global { /// /// - The raw command encoder handle must not be manually destroyed pub unsafe fn command_encoder_as_hal_mut< - A: HalApi, + A: hal::Api, F: FnOnce(Option<&mut A::CommandEncoder>) -> R, R, >( @@ -347,8 +346,8 @@ impl Global { let hub = &self.hub; - let cmd_buf = hub.command_buffers.get(id.into_command_buffer_id()); - let mut cmd_buf_data = cmd_buf.data.lock(); + let cmd_enc = hub.command_encoders.get(id); + let mut cmd_buf_data = cmd_enc.data.lock(); cmd_buf_data.record_as_hal_mut(|opt_cmd_buf| -> R { hal_command_encoder_callback(opt_cmd_buf.and_then(|cmd_buf| { cmd_buf @@ -363,7 +362,7 @@ impl Global { /// # Safety /// /// - The raw queue handle must not be manually destroyed - pub unsafe fn queue_as_hal( + pub unsafe fn queue_as_hal( &self, id: QueueId, ) -> Option> { @@ -377,7 +376,7 @@ impl Global { /// # Safety /// /// - The raw blas handle must not be manually destroyed - pub unsafe fn blas_as_hal( + pub unsafe fn blas_as_hal( &self, id: BlasId, ) -> Option> { @@ -393,7 +392,7 @@ impl Global { /// # Safety /// /// - The raw tlas handle must not be manually destroyed - pub unsafe fn tlas_as_hal( + pub unsafe fn tlas_as_hal( &self, id: TlasId, ) -> Option> { diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index af8bea948a4..489940b77fe 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -21,13 +21,13 @@ use crate::{ device::{ bgl, Device, DeviceError, MissingDownlevelFlags, MissingFeatures, SHADER_STAGE_COUNT, }, - id::{BindGroupLayoutId, BufferId, SamplerId, TextureViewId, TlasId}, + id::{BindGroupLayoutId, BufferId, ExternalTextureId, SamplerId, TextureViewId, TlasId}, init_tracker::{BufferInitTrackerAction, TextureInitTrackerAction}, pipeline::{ComputePipeline, RenderPipeline}, resource::{ - Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError, - MissingTextureUsageError, RawResourceAccess, ResourceErrorIdent, Sampler, TextureView, - Tlas, TrackingData, + Buffer, DestroyedResourceError, ExternalTexture, InvalidResourceError, Labeled, + MissingBufferUsageError, MissingTextureUsageError, RawResourceAccess, ResourceErrorIdent, + Sampler, TextureView, Tlas, TrackingData, }, resource_log, snatch::{SnatchGuard, Snatchable}, @@ -95,8 +95,39 @@ impl WebGpuError for CreateBindGroupLayoutError { } } -//TODO: refactor this to move out `enum BindingError`. +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum BindingError { + #[error(transparent)] + DestroyedResource(#[from] DestroyedResourceError), + #[error("Buffer {buffer}: Binding with size {binding_size} at offset {offset} would overflow buffer size of {buffer_size}")] + BindingRangeTooLarge { + buffer: ResourceErrorIdent, + offset: wgt::BufferAddress, + binding_size: u64, + buffer_size: u64, + }, + #[error("Buffer {buffer}: Binding offset {offset} is greater than buffer size {buffer_size}")] + BindingOffsetTooLarge { + buffer: ResourceErrorIdent, + offset: wgt::BufferAddress, + buffer_size: u64, + }, +} + +impl WebGpuError for BindingError { + fn webgpu_error_type(&self) -> ErrorType { + match self { + Self::DestroyedResource(e) => e.webgpu_error_type(), + Self::BindingRangeTooLarge { .. } | Self::BindingOffsetTooLarge { .. } => { + ErrorType::Validation + } + } + } +} +// TODO: there may be additional variants here that can be extracted into +// `BindingError`. #[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum CreateBindGroupError { @@ -104,6 +135,8 @@ pub enum CreateBindGroupError { Device(#[from] DeviceError), #[error(transparent)] DestroyedResource(#[from] DestroyedResourceError), + #[error(transparent)] + BindingError(#[from] BindingError), #[error( "Binding count declared with at most {expected} items, but {actual} items were provided" )] @@ -114,12 +147,6 @@ pub enum CreateBindGroupError { BindingArrayLengthMismatch { actual: usize, expected: usize }, #[error("Array binding provided zero elements")] BindingArrayZeroLength, - #[error("The bound range {range:?} of {buffer} overflows its size ({size})")] - BindingRangeTooLarge { - buffer: ResourceErrorIdent, - range: Range, - size: u64, - }, #[error("Binding size {actual} of {buffer} is less than minimum {min}")] BindingSizeTooSmall { buffer: ResourceErrorIdent, @@ -140,6 +167,8 @@ pub enum CreateBindGroupError { MissingTextureUsage(#[from] MissingTextureUsageError), #[error("Binding declared as a single item, but bind group is using it as an array")] SingleBindingExpected, + #[error("Effective buffer binding size {size} for storage buffers is expected to align to {alignment}, but size is {size}")] + UnalignedEffectiveBufferBindingSizeForStorage { alignment: u8, size: u64 }, #[error("Buffer offset {0} does not respect device's requested `{1}` limit {2}")] UnalignedBufferOffset(wgt::BufferAddress, &'static str, u32), #[error( @@ -234,6 +263,7 @@ impl WebGpuError for CreateBindGroupError { let e: &dyn WebGpuError = match self { Self::Device(e) => e, Self::DestroyedResource(e) => e, + Self::BindingError(e) => e, Self::MissingBufferUsage(e) => e, Self::MissingTextureUsage(e) => e, Self::ResourceUsageCompatibility(e) => e, @@ -241,13 +271,13 @@ impl WebGpuError for CreateBindGroupError { Self::BindingArrayPartialLengthMismatch { .. } | Self::BindingArrayLengthMismatch { .. } | Self::BindingArrayZeroLength - | Self::BindingRangeTooLarge { .. } | Self::BindingSizeTooSmall { .. } | Self::BindingsNumMismatch { .. } | Self::BindingZeroSize(_) | Self::DuplicateBinding(_) | Self::MissingBindingDeclaration(_) | Self::SingleBindingExpected + | Self::UnalignedEffectiveBufferBindingSizeForStorage { .. } | Self::UnalignedBufferOffset(_, _, _) | Self::BufferRangeTooLarge { .. } | Self::WrongBindingType { .. } @@ -556,9 +586,9 @@ impl BindingTypeMaxCountValidator { if self.has_bindless_array && has_dynamic_offset_array { return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndDynamicOffsetArray); } - if self.has_bindless_array && has_uniform_buffer { - return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer); - } + // if self.has_bindless_array && has_uniform_buffer { + // return Err(CreateBindGroupLayoutError::ContainsBothBindingArrayAndUniformBuffer); + // } Ok(()) } } @@ -567,8 +597,14 @@ impl BindingTypeMaxCountValidator { /// cbindgen:ignore #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -pub struct BindGroupEntry<'a, B = BufferId, S = SamplerId, TV = TextureViewId, TLAS = TlasId> -where +pub struct BindGroupEntry< + 'a, + B = BufferId, + S = SamplerId, + TV = TextureViewId, + TLAS = TlasId, + ET = ExternalTextureId, +> where [BufferBinding]: ToOwned, [S]: ToOwned, [TV]: ToOwned, @@ -581,15 +617,21 @@ where pub binding: u32, #[cfg_attr( feature = "serde", - serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS>: Deserialize<'de>")) + serde(bound(deserialize = "BindingResource<'a, B, S, TV, TLAS, ET>: Deserialize<'de>")) )] /// Resource to attach to the binding - pub resource: BindingResource<'a, B, S, TV, TLAS>, + pub resource: BindingResource<'a, B, S, TV, TLAS, ET>, } /// cbindgen:ignore -pub type ResolvedBindGroupEntry<'a> = - BindGroupEntry<'a, Arc, Arc, Arc, Arc>; +pub type ResolvedBindGroupEntry<'a> = BindGroupEntry< + 'a, + Arc, + Arc, + Arc, + Arc, + Arc, +>; /// Describes a group of bindings and the resources to be bound. #[derive(Clone, Debug)] @@ -601,6 +643,7 @@ pub struct BindGroupDescriptor< S = SamplerId, TV = TextureViewId, TLAS = TlasId, + ET = ExternalTextureId, > where [BufferBinding]: ToOwned, [S]: ToOwned, @@ -608,8 +651,8 @@ pub struct BindGroupDescriptor< <[BufferBinding] as ToOwned>::Owned: fmt::Debug, <[S] as ToOwned>::Owned: fmt::Debug, <[TV] as ToOwned>::Owned: fmt::Debug, - [BindGroupEntry<'a, B, S, TV, TLAS>]: ToOwned, - <[BindGroupEntry<'a, B, S, TV, TLAS>] as ToOwned>::Owned: fmt::Debug, + [BindGroupEntry<'a, B, S, TV, TLAS, ET>]: ToOwned, + <[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: fmt::Debug, { /// Debug label of the bind group. /// @@ -620,11 +663,12 @@ pub struct BindGroupDescriptor< #[cfg_attr( feature = "serde", serde(bound( - deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS>] as ToOwned>::Owned: Deserialize<'de>" + deserialize = "<[BindGroupEntry<'a, B, S, TV, TLAS, ET>] as ToOwned>::Owned: Deserialize<'de>" )) )] /// The resources to bind to this bind group. - pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS>]>, + #[allow(clippy::type_complexity)] + pub entries: Cow<'a, [BindGroupEntry<'a, B, S, TV, TLAS, ET>]>, } /// cbindgen:ignore @@ -635,6 +679,7 @@ pub type ResolvedBindGroupDescriptor<'a> = BindGroupDescriptor< Arc, Arc, Arc, + Arc, >; /// Describes a [`BindGroupLayout`]. @@ -978,8 +1023,14 @@ pub type ResolvedBufferBinding = BufferBinding>; // They're different enough that it doesn't make sense to share a common type #[derive(Debug, Clone)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] -pub enum BindingResource<'a, B = BufferId, S = SamplerId, TV = TextureViewId, TLAS = TlasId> -where +pub enum BindingResource< + 'a, + B = BufferId, + S = SamplerId, + TV = TextureViewId, + TLAS = TlasId, + ET = ExternalTextureId, +> where [BufferBinding]: ToOwned, [S]: ToOwned, [TV]: ToOwned, @@ -1006,10 +1057,17 @@ where )] TextureViewArray(Cow<'a, [TV]>), AccelerationStructure(TLAS), + ExternalTexture(ET), } -pub type ResolvedBindingResource<'a> = - BindingResource<'a, Arc, Arc, Arc, Arc>; +pub type ResolvedBindingResource<'a> = BindingResource< + 'a, + Arc, + Arc, + Arc, + Arc, + Arc, +>; #[derive(Clone, Debug, Error)] #[non_exhaustive] diff --git a/wgpu-core/src/command/bundle.rs b/wgpu-core/src/command/bundle.rs index 743865bb630..8e8bf90b146 100644 --- a/wgpu-core/src/command/bundle.rs +++ b/wgpu-core/src/command/bundle.rs @@ -93,6 +93,7 @@ use core::{ use arrayvec::ArrayVec; use thiserror::Error; +use wgpu_hal::ShouldBeNonZeroExt; use wgt::error::{ErrorType, WebGpuError}; use crate::{ @@ -122,7 +123,7 @@ use crate::{ use super::{ pass, render_command::{ArcRenderCommand, RenderCommand}, - DrawKind, + DrawCommandFamily, DrawKind, }; /// Describes a [`RenderBundleEncoder`]. @@ -379,7 +380,7 @@ impl RenderBundleEncoder { } => { let scope = PassErrorScope::Draw { kind: DrawKind::Draw, - indexed: false, + family: DrawCommandFamily::Draw, }; draw( &mut state, @@ -400,7 +401,7 @@ impl RenderBundleEncoder { } => { let scope = PassErrorScope::Draw { kind: DrawKind::Draw, - indexed: true, + family: DrawCommandFamily::DrawIndexed, }; draw_indexed( &mut state, @@ -413,15 +414,33 @@ impl RenderBundleEncoder { ) .map_pass_err(scope)?; } + RenderCommand::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + } => { + let scope = PassErrorScope::Draw { + kind: DrawKind::Draw, + family: DrawCommandFamily::DrawMeshTasks, + }; + draw_mesh_tasks( + &mut state, + &base.dynamic_offsets, + group_count_x, + group_count_y, + group_count_z, + ) + .map_pass_err(scope)?; + } RenderCommand::DrawIndirect { buffer_id, offset, count: 1, - indexed, + family, } => { let scope = PassErrorScope::Draw { kind: DrawKind::DrawIndirect, - indexed, + family, }; multi_draw_indirect( &mut state, @@ -429,21 +448,25 @@ impl RenderBundleEncoder { &buffer_guard, buffer_id, offset, - indexed, + family, ) .map_pass_err(scope)?; } RenderCommand::DrawIndirect { .. } - | RenderCommand::MultiDrawIndirectCount { .. } => unimplemented!(), - RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(), - RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(), - RenderCommand::PopDebugGroup => unimplemented!(), + | RenderCommand::MultiDrawIndirectCount { .. } + | RenderCommand::PushDebugGroup { color: _, len: _ } + | RenderCommand::InsertDebugMarker { color: _, len: _ } + | RenderCommand::PopDebugGroup => { + unimplemented!("not supported by a render bundle") + } // Must check the TIMESTAMP_QUERY_INSIDE_PASSES feature RenderCommand::WriteTimestamp { .. } | RenderCommand::BeginOcclusionQuery { .. } | RenderCommand::EndOcclusionQuery | RenderCommand::BeginPipelineStatisticsQuery { .. } - | RenderCommand::EndPipelineStatisticsQuery => unimplemented!(), + | RenderCommand::EndPipelineStatisticsQuery => { + unimplemented!("not supported by a render bundle") + } RenderCommand::ExecuteBundle(_) | RenderCommand::SetBlendConstant(_) | RenderCommand::SetStencilReference(_) @@ -602,6 +625,7 @@ fn set_pipeline( Ok(()) } +// This function is duplicative of `render::set_index_buffer`. fn set_index_buffer( state: &mut State, buffer_guard: &crate::storage::Storage>, @@ -620,21 +644,27 @@ fn set_index_buffer( buffer.same_device(&state.device)?; buffer.check_usage(wgt::BufferUsages::INDEX)?; - let end = match size { - Some(s) => offset + s.get(), - None => buffer.size, - }; + if offset % u64::try_from(index_format.byte_size()).unwrap() != 0 { + return Err(RenderCommandError::UnalignedIndexBuffer { + offset, + alignment: index_format.byte_size(), + } + .into()); + } + let end = offset + buffer.resolve_binding_size(offset, size)?; + state .buffer_memory_init_actions .extend(buffer.initialization_status.read().create_action( &buffer, - offset..end, + offset..end.get(), MemoryInitKind::NeedsInitializedMemory, )); - state.set_index_buffer(buffer, index_format, offset..end); + state.set_index_buffer(buffer, index_format, offset..end.get()); Ok(()) } +// This function is duplicative of `render::set_vertex_buffer`. fn set_vertex_buffer( state: &mut State, buffer_guard: &crate::storage::Storage>, @@ -662,18 +692,19 @@ fn set_vertex_buffer( buffer.same_device(&state.device)?; buffer.check_usage(wgt::BufferUsages::VERTEX)?; - let end = match size { - Some(s) => offset + s.get(), - None => buffer.size, - }; + if offset % wgt::VERTEX_ALIGNMENT != 0 { + return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into()); + } + let end = offset + buffer.resolve_binding_size(offset, size)?; + state .buffer_memory_init_actions .extend(buffer.initialization_status.read().create_action( &buffer, - offset..end, + offset..end.get(), MemoryInitKind::NeedsInitializedMemory, )); - state.vertex[slot as usize] = Some(VertexState::new(buffer, offset..end)); + state.vertex[slot as usize] = Some(VertexState::new(buffer, offset..end.get())); Ok(()) } @@ -774,13 +805,48 @@ fn draw_indexed( Ok(()) } +fn draw_mesh_tasks( + state: &mut State, + dynamic_offsets: &[u32], + group_count_x: u32, + group_count_y: u32, + group_count_z: u32, +) -> Result<(), RenderBundleErrorInner> { + let pipeline = state.pipeline()?; + let used_bind_groups = pipeline.used_bind_groups; + + let groups_size_limit = state.device.limits.max_task_workgroups_per_dimension; + let max_groups = state.device.limits.max_task_workgroup_total_count; + if group_count_x > groups_size_limit + || group_count_y > groups_size_limit + || group_count_z > groups_size_limit + || group_count_x * group_count_y * group_count_z > max_groups + { + return Err(RenderBundleErrorInner::Draw(DrawError::InvalidGroupSize { + current: [group_count_x, group_count_y, group_count_z], + limit: groups_size_limit, + max_total: max_groups, + })); + } + + if group_count_x > 0 && group_count_y > 0 && group_count_z > 0 { + state.flush_binds(used_bind_groups, dynamic_offsets); + state.commands.push(ArcRenderCommand::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + }); + } + Ok(()) +} + fn multi_draw_indirect( state: &mut State, dynamic_offsets: &[u32], buffer_guard: &crate::storage::Storage>, buffer_id: id::Id, offset: u64, - indexed: bool, + family: DrawCommandFamily, ) -> Result<(), RenderBundleErrorInner> { state .device @@ -796,7 +862,7 @@ fn multi_draw_indirect( let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps); - let stride = super::get_stride_of_indirect_args(indexed); + let stride = super::get_stride_of_indirect_args(family); state .buffer_memory_init_actions .extend(buffer.initialization_status.read().create_action( @@ -805,7 +871,7 @@ fn multi_draw_indirect( MemoryInitKind::NeedsInitializedMemory, )); - let vertex_or_index_limit = if indexed { + let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed { let index = match state.index { Some(ref mut index) => index, None => return Err(DrawError::MissingIndexBuffer.into()), @@ -831,7 +897,7 @@ fn multi_draw_indirect( buffer, offset, count: 1, - indexed, + family, vertex_or_index_limit, instance_limit, @@ -965,11 +1031,9 @@ impl RenderBundle { size, } => { let buffer = buffer.try_raw(snatch_guard)?; - let bb = hal::BufferBinding { - buffer, - offset: *offset, - size: *size, - }; + // SAFETY: The binding size was checked against the buffer size + // in `set_index_buffer` and again in `IndexState::flush`. + let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size); unsafe { raw.set_index_buffer(bb, *index_format) }; } Cmd::SetVertexBuffer { @@ -979,11 +1043,9 @@ impl RenderBundle { size, } => { let buffer = buffer.try_raw(snatch_guard)?; - let bb = hal::BufferBinding { - buffer, - offset: *offset, - size: *size, - }; + // SAFETY: The binding size was checked against the buffer size + // in `set_vertex_buffer` and again in `VertexState::flush`. + let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size); unsafe { raw.set_vertex_buffer(*slot, bb) }; } Cmd::SetPushConstant { @@ -1057,11 +1119,18 @@ impl RenderBundle { ) }; } + Cmd::DrawMeshTasks { + group_count_x, + group_count_y, + group_count_z, + } => unsafe { + raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z); + }, Cmd::DrawIndirect { buffer, offset, count: 1, - indexed, + family, vertex_or_index_limit, instance_limit, @@ -1072,7 +1141,7 @@ impl RenderBundle { &self.device, buffer, *offset, - *indexed, + *family, *vertex_or_index_limit, *instance_limit, )?; @@ -1083,10 +1152,14 @@ impl RenderBundle { } else { (buffer.try_raw(snatch_guard)?, *offset) }; - if *indexed { - unsafe { raw.draw_indexed_indirect(buffer, offset, 1) }; - } else { - unsafe { raw.draw_indirect(buffer, offset, 1) }; + match family { + DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) }, + DrawCommandFamily::DrawIndexed => unsafe { + raw.draw_indexed_indirect(buffer, offset, 1) + }, + DrawCommandFamily::DrawMeshTasks => unsafe { + raw.draw_mesh_tasks_indirect(buffer, offset, 1); + }, } } Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => { @@ -1131,6 +1204,9 @@ crate::impl_trackable!(RenderBundle); /// [`RenderBundleEncoder::finish`] records the currently set index buffer here, /// and calls [`State::flush_index`] before any indexed draw command to produce /// a `SetIndexBuffer` command if one is necessary. +/// +/// Binding ranges must be validated against the size of the buffer before +/// being stored in `IndexState`. #[derive(Debug)] struct IndexState { buffer: Arc, @@ -1152,13 +1228,21 @@ impl IndexState { /// Generate a `SetIndexBuffer` command to prepare for an indexed draw /// command, if needed. fn flush(&mut self) -> Option { + // This was all checked before, but let's check again just in case. + let binding_size = self + .range + .end + .checked_sub(self.range.start) + .filter(|_| self.range.end <= self.buffer.size) + .expect("index range must be contained in buffer"); + if self.is_dirty { self.is_dirty = false; Some(ArcRenderCommand::SetIndexBuffer { buffer: self.buffer.clone(), index_format: self.format, offset: self.range.start, - size: wgt::BufferSize::new(self.range.end - self.range.start), + size: NonZeroU64::new(binding_size), }) } else { None @@ -1174,6 +1258,9 @@ impl IndexState { /// calls this type's [`flush`] method just before any draw command to /// produce a `SetVertexBuffer` commands if one is necessary. /// +/// Binding ranges must be validated against the size of the buffer before +/// being stored in `VertexState`. +/// /// [`flush`]: IndexState::flush #[derive(Debug)] struct VertexState { @@ -1183,6 +1270,9 @@ struct VertexState { } impl VertexState { + /// Create a new `VertexState`. + /// + /// The `range` must be contained within `buffer`. fn new(buffer: Arc, range: Range) -> Self { Self { buffer, @@ -1195,13 +1285,20 @@ impl VertexState { /// /// `slot` is the index of the vertex buffer slot that `self` tracks. fn flush(&mut self, slot: u32) -> Option { + let binding_size = self + .range + .end + .checked_sub(self.range.start) + .filter(|_| self.range.end <= self.buffer.size) + .expect("vertex range must be contained in buffer"); + if self.is_dirty { self.is_dirty = false; Some(ArcRenderCommand::SetVertexBuffer { slot, buffer: self.buffer.clone(), offset: self.range.start, - size: wgt::BufferSize::new(self.range.end - self.range.start), + size: NonZeroU64::new(binding_size), }) } else { None @@ -1564,7 +1661,7 @@ where pub mod bundle_ffi { use super::{RenderBundleEncoder, RenderCommand}; - use crate::{id, RawString}; + use crate::{command::DrawCommandFamily, id, RawString}; use core::{convert::TryInto, slice}; use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat}; @@ -1719,7 +1816,7 @@ pub mod bundle_ffi { buffer_id, offset, count: 1, - indexed: false, + family: DrawCommandFamily::Draw, }); } @@ -1732,7 +1829,7 @@ pub mod bundle_ffi { buffer_id, offset, count: 1, - indexed: true, + family: DrawCommandFamily::DrawIndexed, }); } diff --git a/wgpu-core/src/command/clear.rs b/wgpu-core/src/command/clear.rs index e18335fb69d..5c8838f69e5 100644 --- a/wgpu-core/src/command/clear.rs +++ b/wgpu-core/src/command/clear.rs @@ -2,17 +2,18 @@ use alloc::{sync::Arc, vec::Vec}; use core::ops::Range; #[cfg(feature = "trace")] -use crate::device::trace::Command as TraceCommand; +use crate::command::Command as TraceCommand; use crate::{ api_log, - command::EncoderStateError, - device::DeviceError, + command::{encoder::EncodingState, ArcCommand, EncoderStateError}, + device::{DeviceError, MissingFeatures}, get_lowest_common_denom, global::Global, + hal_label, id::{BufferId, CommandEncoderId, TextureId}, init_tracker::{MemoryInitKind, TextureInitRange}, resource::{ - DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError, + Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError, ParentDevice, RawResourceAccess, ResourceErrorIdent, Texture, TextureClearMode, }, snatch::SnatchGuard, @@ -30,10 +31,10 @@ use wgt::{ #[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum ClearError { - #[error("To use clear_texture the CLEAR_TEXTURE feature needs to be enabled")] - MissingClearTextureFeature, #[error(transparent)] DestroyedResource(#[from] DestroyedResourceError), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), #[error("{0} can not be cleared")] NoValidTextureClearMode(ResourceErrorIdent), #[error("Buffer clear size {0:?} is not a multiple of `COPY_BUFFER_ALIGNMENT`")] @@ -84,12 +85,12 @@ impl WebGpuError for ClearError { fn webgpu_error_type(&self) -> ErrorType { let e: &dyn WebGpuError = match self { Self::DestroyedResource(e) => e, + Self::MissingFeatures(e) => e, Self::MissingBufferUsage(e) => e, Self::Device(e) => e, Self::EncoderState(e) => e, Self::InvalidResource(e) => e, Self::NoValidTextureClearMode(..) - | Self::MissingClearTextureFeature | Self::UnalignedFillSize(..) | Self::UnalignedBufferOffset(..) | Self::OffsetPlusSizeExceeds64BitBounds { .. } @@ -115,79 +116,20 @@ impl Global { let hub = &self.hub; - let cmd_buf = hub - .command_buffers - .get(command_encoder_id.into_command_buffer_id()); - let mut cmd_buf_data = cmd_buf.data.lock(); - cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> { - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.commands { - list.push(TraceCommand::ClearBuffer { dst, offset, size }); - } - - cmd_buf.device.check_is_valid()?; - - let dst_buffer = hub.buffers.get(dst).get()?; - - dst_buffer.same_device_as(cmd_buf.as_ref())?; - - let dst_pending = cmd_buf_data - .trackers - .buffers - .set_single(&dst_buffer, wgt::BufferUses::COPY_DST); - - let snatch_guard = dst_buffer.device.snatchable_lock.read(); - let dst_raw = dst_buffer.try_raw(&snatch_guard)?; - dst_buffer.check_usage(BufferUsages::COPY_DST)?; - - // Check if offset & size are valid. - if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 { - return Err(ClearError::UnalignedBufferOffset(offset)); - } - - let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset)); - if size % wgt::COPY_BUFFER_ALIGNMENT != 0 { - return Err(ClearError::UnalignedFillSize(size)); - } - let end_offset = - offset - .checked_add(size) - .ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds { - start_offset: offset, - requested_size: size, - })?; - if end_offset > dst_buffer.size { - return Err(ClearError::BufferOverrun { - start_offset: offset, - end_offset, - buffer_size: dst_buffer.size, - }); - } - - if offset == end_offset { - log::trace!("Ignoring fill_buffer of size 0"); - return Ok(()); - } + let cmd_enc = hub.command_encoders.get(command_encoder_id); + let mut cmd_buf_data = cmd_enc.data.lock(); - // Mark dest as initialized. - cmd_buf_data.buffer_memory_init_actions.extend( - dst_buffer.initialization_status.read().create_action( - &dst_buffer, - offset..end_offset, - MemoryInitKind::ImplicitlyInitialized, - ), - ); - - // actual hal barrier & operation - let dst_barrier = - dst_pending.map(|pending| pending.into_hal(&dst_buffer, &snatch_guard)); - let cmd_buf_raw = cmd_buf_data.encoder.open()?; - unsafe { - cmd_buf_raw.transition_buffers(dst_barrier.as_slice()); - cmd_buf_raw.clear_buffer(dst_raw, offset..end_offset); - } + #[cfg(feature = "trace")] + if let Some(ref mut list) = cmd_buf_data.trace() { + list.push(TraceCommand::ClearBuffer { dst, offset, size }); + } - Ok(()) + cmd_buf_data.push_with(|| -> Result<_, ClearError> { + Ok(ArcCommand::ClearBuffer { + dst: self.resolve_buffer_id(dst)?, + offset, + size, + }) }) } @@ -202,87 +144,163 @@ impl Global { let hub = &self.hub; - let cmd_buf = hub - .command_buffers - .get(command_encoder_id.into_command_buffer_id()); - let mut cmd_buf_data = cmd_buf.data.lock(); - cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), ClearError> { - #[cfg(feature = "trace")] - if let Some(ref mut list) = cmd_buf_data.commands { - list.push(TraceCommand::ClearTexture { - dst, - subresource_range: *subresource_range, - }); - } + let cmd_enc = hub.command_encoders.get(command_encoder_id); + let mut cmd_buf_data = cmd_enc.data.lock(); - cmd_buf.device.check_is_valid()?; + #[cfg(feature = "trace")] + if let Some(ref mut list) = cmd_buf_data.trace() { + list.push(TraceCommand::ClearTexture { + dst, + subresource_range: *subresource_range, + }); + } - if !cmd_buf.support_clear_texture { - return Err(ClearError::MissingClearTextureFeature); - } + cmd_buf_data.push_with(|| -> Result<_, ClearError> { + Ok(ArcCommand::ClearTexture { + dst: self.resolve_texture_id(dst)?, + subresource_range: *subresource_range, + }) + }) + } +} - let dst_texture = hub.textures.get(dst).get()?; +pub(super) fn clear_buffer( + state: &mut EncodingState, + dst_buffer: Arc, + offset: BufferAddress, + size: Option, +) -> Result<(), ClearError> { + dst_buffer.same_device(state.device)?; - dst_texture.same_device_as(cmd_buf.as_ref())?; + let dst_pending = state + .tracker + .buffers + .set_single(&dst_buffer, wgt::BufferUses::COPY_DST); - // Check if subresource aspects are valid. - let clear_aspects = - hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect); - if clear_aspects.is_empty() { - return Err(ClearError::MissingTextureAspect { - texture_format: dst_texture.desc.format, - subresource_range_aspects: subresource_range.aspect, - }); - }; + let dst_raw = dst_buffer.try_raw(state.snatch_guard)?; + dst_buffer.check_usage(BufferUsages::COPY_DST)?; - // Check if subresource level range is valid - let subresource_mip_range = - subresource_range.mip_range(dst_texture.full_range.mips.end); - if dst_texture.full_range.mips.start > subresource_mip_range.start - || dst_texture.full_range.mips.end < subresource_mip_range.end - { - return Err(ClearError::InvalidTextureLevelRange { - texture_level_range: dst_texture.full_range.mips.clone(), - subresource_base_mip_level: subresource_range.base_mip_level, - subresource_mip_level_count: subresource_range.mip_level_count, - }); - } - // Check if subresource layer range is valid - let subresource_layer_range = - subresource_range.layer_range(dst_texture.full_range.layers.end); - if dst_texture.full_range.layers.start > subresource_layer_range.start - || dst_texture.full_range.layers.end < subresource_layer_range.end - { - return Err(ClearError::InvalidTextureLayerRange { - texture_layer_range: dst_texture.full_range.layers.clone(), - subresource_base_array_layer: subresource_range.base_array_layer, - subresource_array_layer_count: subresource_range.array_layer_count, - }); - } + // Check if offset & size are valid. + if offset % wgt::COPY_BUFFER_ALIGNMENT != 0 { + return Err(ClearError::UnalignedBufferOffset(offset)); + } - let device = &cmd_buf.device; - device.check_is_valid()?; - let (encoder, tracker) = cmd_buf_data.open_encoder_and_tracker()?; - - let snatch_guard = device.snatchable_lock.read(); - clear_texture( - &dst_texture, - TextureInitRange { - mip_range: subresource_mip_range, - layer_range: subresource_layer_range, - }, - encoder, - &mut tracker.textures, - &device.alignments, - device.zero_buffer.as_ref(), - &snatch_guard, - )?; - - Ok(()) - }) + let size = size.unwrap_or(dst_buffer.size.saturating_sub(offset)); + if size % wgt::COPY_BUFFER_ALIGNMENT != 0 { + return Err(ClearError::UnalignedFillSize(size)); + } + let end_offset = + offset + .checked_add(size) + .ok_or(ClearError::OffsetPlusSizeExceeds64BitBounds { + start_offset: offset, + requested_size: size, + })?; + if end_offset > dst_buffer.size { + return Err(ClearError::BufferOverrun { + start_offset: offset, + end_offset, + buffer_size: dst_buffer.size, + }); + } + + if offset == end_offset { + log::trace!("Ignoring fill_buffer of size 0"); + return Ok(()); + } + + // Mark dest as initialized. + state + .buffer_memory_init_actions + .extend(dst_buffer.initialization_status.read().create_action( + &dst_buffer, + offset..end_offset, + MemoryInitKind::ImplicitlyInitialized, + )); + + // actual hal barrier & operation + let dst_barrier = dst_pending.map(|pending| pending.into_hal(&dst_buffer, state.snatch_guard)); + unsafe { + state.raw_encoder.transition_buffers(dst_barrier.as_slice()); + state.raw_encoder.clear_buffer(dst_raw, offset..end_offset); + } + + Ok(()) +} + +/// Validate and encode a "Clear Texture" command. +/// +/// This function implements `CommandEncoder::clear_texture` when invoked via +/// the command encoder APIs or trace playback. It has the suffix `_cmd` to +/// distinguish it from [`clear_texture`]. [`clear_texture`], used internally by +/// this function, is a lower-level function that encodes a texture clear +/// operation without validating it. +pub(super) fn clear_texture_cmd( + state: &mut EncodingState, + dst_texture: Arc, + subresource_range: &ImageSubresourceRange, +) -> Result<(), ClearError> { + dst_texture.same_device(state.device)?; + state + .device + .require_features(wgt::Features::CLEAR_TEXTURE)?; + + // Check if subresource aspects are valid. + let clear_aspects = hal::FormatAspects::new(dst_texture.desc.format, subresource_range.aspect); + if clear_aspects.is_empty() { + return Err(ClearError::MissingTextureAspect { + texture_format: dst_texture.desc.format, + subresource_range_aspects: subresource_range.aspect, + }); + }; + + // Check if subresource level range is valid + let subresource_mip_range = subresource_range.mip_range(dst_texture.full_range.mips.end); + if dst_texture.full_range.mips.start > subresource_mip_range.start + || dst_texture.full_range.mips.end < subresource_mip_range.end + { + return Err(ClearError::InvalidTextureLevelRange { + texture_level_range: dst_texture.full_range.mips.clone(), + subresource_base_mip_level: subresource_range.base_mip_level, + subresource_mip_level_count: subresource_range.mip_level_count, + }); } + // Check if subresource layer range is valid + let subresource_layer_range = subresource_range.layer_range(dst_texture.full_range.layers.end); + if dst_texture.full_range.layers.start > subresource_layer_range.start + || dst_texture.full_range.layers.end < subresource_layer_range.end + { + return Err(ClearError::InvalidTextureLayerRange { + texture_layer_range: dst_texture.full_range.layers.clone(), + subresource_base_array_layer: subresource_range.base_array_layer, + subresource_array_layer_count: subresource_range.array_layer_count, + }); + } + + clear_texture( + &dst_texture, + TextureInitRange { + mip_range: subresource_mip_range, + layer_range: subresource_layer_range, + }, + state.raw_encoder, + &mut state.tracker.textures, + &state.device.alignments, + state.device.zero_buffer.as_ref(), + state.snatch_guard, + state.device.instance_flags, + )?; + + Ok(()) } +/// Encode a texture clear operation. +/// +/// This function encodes a texture clear operation without validating it. +/// Texture clears requested via the API call this function via +/// [`clear_texture_cmd`], which does the validation. This function is also +/// called directly from various places within wgpu that need to clear a +/// texture. pub(crate) fn clear_texture( dst_texture: &Arc, range: TextureInitRange, @@ -291,6 +309,7 @@ pub(crate) fn clear_texture( alignments: &hal::Alignments, zero_buffer: &dyn hal::DynBuffer, snatch_guard: &SnatchGuard<'_>, + instance_flags: wgt::InstanceFlags, ) -> Result<(), ClearError> { let dst_raw = dst_texture.try_raw(snatch_guard)?; @@ -349,11 +368,11 @@ pub(crate) fn clear_texture( ), TextureClearMode::Surface { .. } => { drop(clear_mode); - clear_texture_via_render_passes(dst_texture, range, true, encoder)? + clear_texture_via_render_passes(dst_texture, range, true, encoder, instance_flags)? } TextureClearMode::RenderPass { is_color, .. } => { drop(clear_mode); - clear_texture_via_render_passes(dst_texture, range, is_color, encoder)? + clear_texture_via_render_passes(dst_texture, range, is_color, encoder, instance_flags)? } TextureClearMode::None => { return Err(ClearError::NoValidTextureClearMode( @@ -374,8 +393,10 @@ fn clear_texture_via_buffer_copies( ) { assert!(!texture_desc.format.is_depth_stencil_format()); - if texture_desc.format == wgt::TextureFormat::NV12 { - // TODO: Currently COPY_DST for NV12 textures is unsupported. + if texture_desc.format == wgt::TextureFormat::NV12 + || texture_desc.format == wgt::TextureFormat::P010 + { + // TODO: Currently COPY_DST for NV12 and P010 textures is unsupported. return; } @@ -463,6 +484,7 @@ fn clear_texture_via_render_passes( range: TextureInitRange, is_color: bool, encoder: &mut dyn hal::DynCommandEncoder, + instance_flags: wgt::InstanceFlags, ) -> Result<(), ClearError> { assert_eq!(dst_texture.desc.dimension, wgt::TextureDimension::D2); @@ -517,7 +539,10 @@ fn clear_texture_via_render_passes( unsafe { encoder .begin_render_pass(&hal::RenderPassDescriptor { - label: Some("(wgpu internal) clear_texture clear pass"), + label: hal_label( + Some("(wgpu internal) clear_texture clear pass"), + instance_flags, + ), extent, sample_count: dst_texture.desc.sample_count, color_attachments, diff --git a/wgpu-core/src/command/compute.rs b/wgpu-core/src/command/compute.rs index 361a5990f81..515ca76e8f6 100644 --- a/wgpu-core/src/command/compute.rs +++ b/wgpu-core/src/command/compute.rs @@ -5,10 +5,8 @@ use wgt::{ }; use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec}; -use core::{fmt, str}; +use core::{convert::Infallible, fmt, str}; -use crate::command::{pass, EncoderStateError, PassStateError, TimestampWritesError}; -use crate::resource::DestroyedResourceError; use crate::{binding_model::BindError, resource::RawResourceAccess}; use crate::{ binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError}, @@ -18,8 +16,8 @@ use crate::{ end_pipeline_statistics_query, memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState}, pass_base, pass_try, validate_and_begin_pipeline_statistics_query, ArcPassTimestampWrites, - BasePass, BindGroupStateChange, CommandBuffer, CommandEncoderError, MapPassErr, - PassErrorScope, PassTimestampWrites, QueryUseError, StateChange, + BasePass, BindGroupStateChange, CommandEncoderError, MapPassErr, PassErrorScope, + PassTimestampWrites, QueryUseError, StateChange, }, device::{DeviceError, MissingDownlevelFlags, MissingFeatures}, global::Global, @@ -32,6 +30,14 @@ use crate::{ track::{ResourceUsageCompatibilityError, Tracker, TrackerIndex}, Label, }; +use crate::{command::InnerCommandEncoder, resource::DestroyedResourceError}; +use crate::{ + command::{ + encoder::EncodingState, pass, ArcCommand, CommandEncoder, DebugGroupError, + EncoderStateError, PassStateError, TimestampWritesError, + }, + device::Device, +}; pub type ComputeBasePass = BasePass; @@ -46,12 +52,12 @@ pub struct ComputePass { /// All pass data & records is stored here. base: ComputeBasePass, - /// Parent command buffer that this pass records commands into. + /// Parent command encoder that this pass records commands into. /// /// If this is `Some`, then the pass is in WebGPU's "open" state. If it is /// `None`, then the pass is in the "ended" state. /// See - parent: Option>, + parent: Option>, timestamp_writes: Option, @@ -61,8 +67,8 @@ pub struct ComputePass { } impl ComputePass { - /// If the parent command buffer is invalid, the returned pass will be invalid. - fn new(parent: Arc, desc: ArcComputePassDescriptor) -> Self { + /// If the parent command encoder is invalid, the returned pass will be invalid. + fn new(parent: Arc, desc: ArcComputePassDescriptor) -> Self { let ArcComputePassDescriptor { label, timestamp_writes, @@ -78,7 +84,7 @@ impl ComputePass { } } - fn new_invalid(parent: Arc, label: &Label, err: ComputePassError) -> Self { + fn new_invalid(parent: Arc, label: &Label, err: ComputePassError) -> Self { Self { base: BasePass::new_invalid(label, err), parent: Some(parent), @@ -97,7 +103,7 @@ impl ComputePass { impl fmt::Debug for ComputePass { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self.parent { - Some(ref cmd_buf) => write!(f, "ComputePass {{ parent: {} }}", cmd_buf.error_ident()), + Some(ref cmd_enc) => write!(f, "ComputePass {{ parent: {} }}", cmd_enc.error_ident()), None => write!(f, "ComputePass {{ parent: None }}"), } } @@ -144,6 +150,8 @@ pub enum ComputePassErrorInner { #[error("Parent encoder is invalid")] InvalidParentEncoder, #[error(transparent)] + DebugGroupError(#[from] DebugGroupError), + #[error(transparent)] BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange), #[error(transparent)] DestroyedResource(#[from] DestroyedResourceError), @@ -160,8 +168,6 @@ pub enum ComputePassErrorInner { #[error(transparent)] MissingBufferUsage(#[from] MissingBufferUsageError), #[error(transparent)] - InvalidPopDebugGroup(#[from] pass::InvalidPopDebugGroup), - #[error(transparent)] Dispatch(#[from] DispatchError), #[error(transparent)] Bind(#[from] BindError), @@ -224,6 +230,7 @@ impl WebGpuError for ComputePassError { let e: &dyn WebGpuError = match inner { ComputePassErrorInner::Device(e) => e, ComputePassErrorInner::EncoderState(e) => e, + ComputePassErrorInner::DebugGroupError(e) => e, ComputePassErrorInner::DestroyedResource(e) => e, ComputePassErrorInner::ResourceUsageCompatibility(e) => e, ComputePassErrorInner::MissingBufferUsage(e) => e, @@ -236,7 +243,6 @@ impl WebGpuError for ComputePassError { ComputePassErrorInner::InvalidResource(e) => e, ComputePassErrorInner::TimestampWrites(e) => e, ComputePassErrorInner::InvalidValuesOffset(e) => e, - ComputePassErrorInner::InvalidPopDebugGroup(e) => e, ComputePassErrorInner::InvalidParentEncoder | ComputePassErrorInner::BindGroupIndexOutOfRange { .. } @@ -251,10 +257,10 @@ impl WebGpuError for ComputePassError { } } -struct State<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> { +struct State<'scope, 'snatch_guard, 'cmd_enc> { pipeline: Option>, - general: pass::BaseState<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder>, + pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>, active_query: Option<(Arc, u32)>, @@ -263,13 +269,11 @@ struct State<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> { intermediate_trackers: Tracker, } -impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> - State<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> -{ +impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> { fn is_ready(&self) -> Result<(), DispatchError> { if let Some(pipeline) = self.pipeline.as_ref() { - self.general.binder.check_compatibility(pipeline.as_ref())?; - self.general.binder.check_late_buffer_bindings()?; + self.pass.binder.check_compatibility(pipeline.as_ref())?; + self.pass.binder.check_late_buffer_bindings()?; Ok(()) } else { Err(DispatchError::MissingPipeline(pass::MissingPipeline)) @@ -282,19 +286,16 @@ impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> &mut self, indirect_buffer: Option, ) -> Result<(), ResourceUsageCompatibilityError> { - for bind_group in self.general.binder.list_active() { - unsafe { self.general.scope.merge_bind_group(&bind_group.used)? }; + for bind_group in self.pass.binder.list_active() { + unsafe { self.pass.scope.merge_bind_group(&bind_group.used)? }; // Note: stateless trackers are not merged: the lifetime reference // is held to the bind group itself. } - for bind_group in self.general.binder.list_active() { + for bind_group in self.pass.binder.list_active() { unsafe { self.intermediate_trackers - .set_and_remove_from_usage_scope_sparse( - &mut self.general.scope, - &bind_group.used, - ) + .set_and_remove_from_usage_scope_sparse(&mut self.pass.scope, &bind_group.used) } } @@ -303,15 +304,15 @@ impl<'scope, 'snatch_guard, 'cmd_buf, 'raw_encoder> self.intermediate_trackers .buffers .set_and_remove_from_usage_scope_sparse( - &mut self.general.scope.buffers, + &mut self.pass.scope.buffers, indirect_buffer, ); } - CommandBuffer::drain_barriers( - self.general.raw_encoder, + CommandEncoder::drain_barriers( + self.pass.base.raw_encoder, &mut self.intermediate_trackers, - self.general.snatch_guard, + self.pass.base.snatch_guard, ); Ok(()) } @@ -342,15 +343,15 @@ impl Global { let label = desc.label.as_deref().map(Cow::Borrowed); - let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id()); - let mut cmd_buf_data = cmd_buf.data.lock(); + let cmd_enc = hub.command_encoders.get(encoder_id); + let mut cmd_buf_data = cmd_enc.data.lock(); match cmd_buf_data.lock_encoder() { Ok(()) => { drop(cmd_buf_data); - if let Err(err) = cmd_buf.device.check_is_valid() { + if let Err(err) = cmd_enc.device.check_is_valid() { return ( - ComputePass::new_invalid(cmd_buf, &label, err.map_pass_err(scope)), + ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)), None, ); } @@ -360,7 +361,7 @@ impl Global { .as_ref() .map(|tw| { Self::validate_pass_timestamp_writes::( - &cmd_buf.device, + &cmd_enc.device, &hub.query_sets.read(), tw, ) @@ -372,10 +373,10 @@ impl Global { label, timestamp_writes, }; - (ComputePass::new(cmd_buf, arc_desc), None) + (ComputePass::new(cmd_enc, arc_desc), None) } Err(err) => ( - ComputePass::new_invalid(cmd_buf, &label, err.map_pass_err(scope)), + ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)), None, ), } @@ -387,7 +388,7 @@ impl Global { cmd_buf_data.invalidate(err.clone()); drop(cmd_buf_data); ( - ComputePass::new_invalid(cmd_buf, &label, err.map_pass_err(scope)), + ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)), None, ) } @@ -396,7 +397,7 @@ impl Global { // generates an immediate validation error. drop(cmd_buf_data); ( - ComputePass::new_invalid(cmd_buf, &label, err.clone().map_pass_err(scope)), + ComputePass::new_invalid(cmd_enc, &label, err.clone().map_pass_err(scope)), Some(err.into()), ) } @@ -408,7 +409,7 @@ impl Global { // invalid pass to save that work. drop(cmd_buf_data); ( - ComputePass::new_invalid(cmd_buf, &label, err.map_pass_err(scope)), + ComputePass::new_invalid(cmd_enc, &label, err.map_pass_err(scope)), None, ) } @@ -428,20 +429,17 @@ impl Global { pub fn compute_pass_end_with_unresolved_commands( &self, encoder_id: id::CommandEncoderId, - base: BasePass, + base: BasePass, timestamp_writes: Option<&PassTimestampWrites>, ) { #[cfg(feature = "trace")] { - let cmd_buf = self - .hub - .command_buffers - .get(encoder_id.into_command_buffer_id()); - let mut cmd_buf_data = cmd_buf.data.lock(); + let cmd_enc = self.hub.command_encoders.get(encoder_id); + let mut cmd_buf_data = cmd_enc.data.lock(); let cmd_buf_data = cmd_buf_data.get_inner(); - if let Some(ref mut list) = cmd_buf_data.commands { - list.push(crate::device::trace::Command::RunComputePass { + if let Some(ref mut list) = cmd_buf_data.trace_commands { + list.push(crate::command::Command::RunComputePass { base: BasePass { label: base.label.clone(), error: None, @@ -489,308 +487,333 @@ impl Global { } pub fn compute_pass_end(&self, pass: &mut ComputePass) -> Result<(), EncoderStateError> { - let pass_scope = PassErrorScope::Pass; profiling::scope!( "CommandEncoder::run_compute_pass {}", - base.label.as_deref().unwrap_or("") + pass.base.label.as_deref().unwrap_or("") ); - let cmd_buf = pass.parent.take().ok_or(EncoderStateError::Ended)?; - let mut cmd_buf_data = cmd_buf.data.lock(); + let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?; + let mut cmd_buf_data = cmd_enc.data.lock(); - if let Some(err) = pass.base.error.take() { - if matches!( - err, - ComputePassError { - inner: ComputePassErrorInner::EncoderState(EncoderStateError::Ended), - scope: _, - } - ) { - // If the encoder was already finished at time of pass creation, - // then it was not put in the locked state, so we need to - // generate a validation error here due to the encoder not being - // locked. The encoder already has a copy of the error. - return Err(EncoderStateError::Ended); - } else { - // If the pass is invalid, invalidate the parent encoder and return. - // Since we do not track the state of an invalid encoder, it is not - // necessary to unlock it. - cmd_buf_data.invalidate(err); - return Ok(()); - } - } + cmd_buf_data.unlock_encoder()?; - cmd_buf_data.unlock_and_record(|cmd_buf_data| -> Result<(), ComputePassError> { - let device = &cmd_buf.device; - device.check_is_valid().map_pass_err(pass_scope)?; + let base = pass.base.take(); - let base = &mut pass.base; - - let encoder = &mut cmd_buf_data.encoder; - - // We automatically keep extending command buffers over time, and because - // we want to insert a command buffer _before_ what we're about to record, - // we need to make sure to close the previous one. - encoder.close_if_open().map_pass_err(pass_scope)?; - let raw_encoder = encoder - .open_pass(base.label.as_deref()) - .map_pass_err(pass_scope)?; + if matches!( + base, + Err(ComputePassError { + inner: ComputePassErrorInner::EncoderState(EncoderStateError::Ended), + scope: _, + }) + ) { + // If the encoder was already finished at time of pass creation, + // then it was not put in the locked state, so we need to + // generate a validation error here and now due to the encoder not + // being locked. The encoder already holds an error from when the + // pass was opened, or earlier. + // + // All other errors are propagated to the encoder within `push_with`, + // and will be reported later. + return Err(EncoderStateError::Ended); + } - let snatch_guard = device.snatchable_lock.read(); + cmd_buf_data.push_with(|| -> Result<_, ComputePassError> { + Ok(ArcCommand::RunComputePass { + pass: base?, + timestamp_writes: pass.timestamp_writes.take(), + }) + }) + } +} - let mut state = State { - pipeline: None, +pub(super) fn encode_compute_pass( + parent_state: &mut EncodingState, + mut base: BasePass, + mut timestamp_writes: Option, +) -> Result<(), ComputePassError> { + let pass_scope = PassErrorScope::Pass; + + let device = parent_state.device; + + // We automatically keep extending command buffers over time, and because + // we want to insert a command buffer _before_ what we're about to record, + // we need to make sure to close the previous one. + parent_state + .raw_encoder + .close_if_open() + .map_pass_err(pass_scope)?; + let raw_encoder = parent_state + .raw_encoder + .open_pass(base.label.as_deref()) + .map_pass_err(pass_scope)?; + + let mut debug_scope_depth = 0; + + let mut state = State { + pipeline: None, + + pass: pass::PassState { + base: EncodingState { + device, + raw_encoder, + tracker: parent_state.tracker, + buffer_memory_init_actions: parent_state.buffer_memory_init_actions, + texture_memory_actions: parent_state.texture_memory_actions, + as_actions: parent_state.as_actions, + temp_resources: parent_state.temp_resources, + indirect_draw_validation_resources: parent_state.indirect_draw_validation_resources, + snatch_guard: parent_state.snatch_guard, + debug_scope_depth: &mut debug_scope_depth, + }, + binder: Binder::new(), + temp_offsets: Vec::new(), + dynamic_offset_count: 0, + pending_discard_init_fixups: SurfacesInDiscardState::new(), + scope: device.new_usage_scope(), + string_offset: 0, + }, + active_query: None, - general: pass::BaseState { - device, - raw_encoder, - tracker: &mut cmd_buf_data.trackers, - buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions, - texture_memory_actions: &mut cmd_buf_data.texture_memory_actions, - as_actions: &mut cmd_buf_data.as_actions, - binder: Binder::new(), - temp_offsets: Vec::new(), - dynamic_offset_count: 0, + push_constants: Vec::new(), - pending_discard_init_fixups: SurfacesInDiscardState::new(), + intermediate_trackers: Tracker::new(), + }; - snatch_guard: &snatch_guard, - scope: device.new_usage_scope(), + let indices = &device.tracker_indices; + state + .pass + .base + .tracker + .buffers + .set_size(indices.buffers.size()); + state + .pass + .base + .tracker + .textures + .set_size(indices.textures.size()); - debug_scope_depth: 0, - string_offset: 0, - }, - active_query: None, + let timestamp_writes: Option> = + if let Some(tw) = timestamp_writes.take() { + tw.query_set.same_device(device).map_pass_err(pass_scope)?; - push_constants: Vec::new(), + let query_set = state + .pass + .base + .tracker + .query_sets + .insert_single(tw.query_set); - intermediate_trackers: Tracker::new(), + // Unlike in render passes we can't delay resetting the query sets since + // there is no auxiliary pass. + let range = if let (Some(index_a), Some(index_b)) = + (tw.beginning_of_pass_write_index, tw.end_of_pass_write_index) + { + Some(index_a.min(index_b)..index_a.max(index_b) + 1) + } else { + tw.beginning_of_pass_write_index + .or(tw.end_of_pass_write_index) + .map(|i| i..i + 1) }; + // Range should always be Some, both values being None should lead to a validation error. + // But no point in erroring over that nuance here! + if let Some(range) = range { + unsafe { + state + .pass + .base + .raw_encoder + .reset_queries(query_set.raw(), range); + } + } - let indices = &state.general.device.tracker_indices; - state - .general - .tracker - .buffers - .set_size(indices.buffers.size()); - state - .general - .tracker - .textures - .set_size(indices.textures.size()); - - let timestamp_writes: Option> = - if let Some(tw) = pass.timestamp_writes.take() { - tw.query_set - .same_device_as(cmd_buf.as_ref()) - .map_pass_err(pass_scope)?; - - let query_set = state.general.tracker.query_sets.insert_single(tw.query_set); - - // Unlike in render passes we can't delay resetting the query sets since - // there is no auxiliary pass. - let range = if let (Some(index_a), Some(index_b)) = - (tw.beginning_of_pass_write_index, tw.end_of_pass_write_index) - { - Some(index_a.min(index_b)..index_a.max(index_b) + 1) - } else { - tw.beginning_of_pass_write_index - .or(tw.end_of_pass_write_index) - .map(|i| i..i + 1) - }; - // Range should always be Some, both values being None should lead to a validation error. - // But no point in erroring over that nuance here! - if let Some(range) = range { - unsafe { - state - .general - .raw_encoder - .reset_queries(query_set.raw(), range); - } - } + Some(hal::PassTimestampWrites { + query_set: query_set.raw(), + beginning_of_pass_write_index: tw.beginning_of_pass_write_index, + end_of_pass_write_index: tw.end_of_pass_write_index, + }) + } else { + None + }; - Some(hal::PassTimestampWrites { - query_set: query_set.raw(), - beginning_of_pass_write_index: tw.beginning_of_pass_write_index, - end_of_pass_write_index: tw.end_of_pass_write_index, - }) - } else { - None - }; + let hal_desc = hal::ComputePassDescriptor { + label: hal_label(base.label.as_deref(), device.instance_flags), + timestamp_writes, + }; - let hal_desc = hal::ComputePassDescriptor { - label: hal_label(base.label.as_deref(), device.instance_flags), - timestamp_writes, - }; + unsafe { + state.pass.base.raw_encoder.begin_compute_pass(&hal_desc); + } - unsafe { - state.general.raw_encoder.begin_compute_pass(&hal_desc); + for command in base.commands.drain(..) { + match command { + ArcComputeCommand::SetBindGroup { + index, + num_dynamic_offsets, + bind_group, + } => { + let scope = PassErrorScope::SetBindGroup; + pass::set_bind_group::( + &mut state.pass, + device, + &base.dynamic_offsets, + index, + num_dynamic_offsets, + bind_group, + false, + ) + .map_pass_err(scope)?; } - - for command in base.commands.drain(..) { - match command { - ArcComputeCommand::SetBindGroup { - index, - num_dynamic_offsets, - bind_group, - } => { - let scope = PassErrorScope::SetBindGroup; - pass::set_bind_group::( - &mut state.general, - cmd_buf.as_ref(), - &base.dynamic_offsets, - index, - num_dynamic_offsets, - bind_group, - false, - ) - .map_pass_err(scope)?; - } - ArcComputeCommand::SetPipeline(pipeline) => { - let scope = PassErrorScope::SetPipelineCompute; - set_pipeline(&mut state, cmd_buf.as_ref(), pipeline).map_pass_err(scope)?; - } - ArcComputeCommand::SetPushConstant { - offset, - size_bytes, - values_offset, - } => { - let scope = PassErrorScope::SetPushConstant; - pass::set_push_constant::( - &mut state.general, - &base.push_constant_data, - wgt::ShaderStages::COMPUTE, - offset, - size_bytes, - Some(values_offset), - |data_slice| { - let offset_in_elements = - (offset / wgt::PUSH_CONSTANT_ALIGNMENT) as usize; - let size_in_elements = - (size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize; - state.push_constants[offset_in_elements..][..size_in_elements] - .copy_from_slice(data_slice); - }, - ) - .map_pass_err(scope)?; - } - ArcComputeCommand::Dispatch(groups) => { - let scope = PassErrorScope::Dispatch { indirect: false }; - dispatch(&mut state, groups).map_pass_err(scope)?; - } - ArcComputeCommand::DispatchIndirect { buffer, offset } => { - let scope = PassErrorScope::Dispatch { indirect: true }; - dispatch_indirect(&mut state, cmd_buf.as_ref(), buffer, offset) - .map_pass_err(scope)?; - } - ArcComputeCommand::PushDebugGroup { color: _, len } => { - pass::push_debug_group(&mut state.general, &base.string_data, len); - } - ArcComputeCommand::PopDebugGroup => { - let scope = PassErrorScope::PopDebugGroup; - pass::pop_debug_group::(&mut state.general) - .map_pass_err(scope)?; - } - ArcComputeCommand::InsertDebugMarker { color: _, len } => { - pass::insert_debug_marker(&mut state.general, &base.string_data, len); - } - ArcComputeCommand::WriteTimestamp { - query_set, - query_index, - } => { - let scope = PassErrorScope::WriteTimestamp; - pass::write_timestamp::( - &mut state.general, - cmd_buf.as_ref(), - None, - query_set, - query_index, - ) - .map_pass_err(scope)?; - } - ArcComputeCommand::BeginPipelineStatisticsQuery { - query_set, - query_index, - } => { - let scope = PassErrorScope::BeginPipelineStatisticsQuery; - validate_and_begin_pipeline_statistics_query( - query_set, - state.general.raw_encoder, - &mut state.general.tracker.query_sets, - cmd_buf.as_ref(), - query_index, - None, - &mut state.active_query, - ) - .map_pass_err(scope)?; - } - ArcComputeCommand::EndPipelineStatisticsQuery => { - let scope = PassErrorScope::EndPipelineStatisticsQuery; - end_pipeline_statistics_query( - state.general.raw_encoder, - &mut state.active_query, - ) - .map_pass_err(scope)?; - } - } + ArcComputeCommand::SetPipeline(pipeline) => { + let scope = PassErrorScope::SetPipelineCompute; + set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?; } - - unsafe { - state.general.raw_encoder.end_compute_pass(); + ArcComputeCommand::SetPushConstant { + offset, + size_bytes, + values_offset, + } => { + let scope = PassErrorScope::SetPushConstant; + pass::set_push_constant::( + &mut state.pass, + &base.push_constant_data, + wgt::ShaderStages::COMPUTE, + offset, + size_bytes, + Some(values_offset), + |data_slice| { + let offset_in_elements = (offset / wgt::PUSH_CONSTANT_ALIGNMENT) as usize; + let size_in_elements = (size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize; + state.push_constants[offset_in_elements..][..size_in_elements] + .copy_from_slice(data_slice); + }, + ) + .map_pass_err(scope)?; + } + ArcComputeCommand::Dispatch(groups) => { + let scope = PassErrorScope::Dispatch { indirect: false }; + dispatch(&mut state, groups).map_pass_err(scope)?; + } + ArcComputeCommand::DispatchIndirect { buffer, offset } => { + let scope = PassErrorScope::Dispatch { indirect: true }; + dispatch_indirect(&mut state, device, buffer, offset).map_pass_err(scope)?; + } + ArcComputeCommand::PushDebugGroup { color: _, len } => { + pass::push_debug_group(&mut state.pass, &base.string_data, len); + } + ArcComputeCommand::PopDebugGroup => { + let scope = PassErrorScope::PopDebugGroup; + pass::pop_debug_group::(&mut state.pass) + .map_pass_err(scope)?; + } + ArcComputeCommand::InsertDebugMarker { color: _, len } => { + pass::insert_debug_marker(&mut state.pass, &base.string_data, len); + } + ArcComputeCommand::WriteTimestamp { + query_set, + query_index, + } => { + let scope = PassErrorScope::WriteTimestamp; + pass::write_timestamp::( + &mut state.pass, + device, + None, // compute passes do not attempt to coalesce query resets + query_set, + query_index, + ) + .map_pass_err(scope)?; + } + ArcComputeCommand::BeginPipelineStatisticsQuery { + query_set, + query_index, + } => { + let scope = PassErrorScope::BeginPipelineStatisticsQuery; + validate_and_begin_pipeline_statistics_query( + query_set, + state.pass.base.raw_encoder, + &mut state.pass.base.tracker.query_sets, + device, + query_index, + None, + &mut state.active_query, + ) + .map_pass_err(scope)?; + } + ArcComputeCommand::EndPipelineStatisticsQuery => { + let scope = PassErrorScope::EndPipelineStatisticsQuery; + end_pipeline_statistics_query(state.pass.base.raw_encoder, &mut state.active_query) + .map_pass_err(scope)?; } + } + } - let State { - general: - pass::BaseState { - tracker, - pending_discard_init_fixups, - .. - }, - intermediate_trackers, - .. - } = state; + if *state.pass.base.debug_scope_depth > 0 { + Err( + ComputePassErrorInner::DebugGroupError(DebugGroupError::MissingPop) + .map_pass_err(pass_scope), + )?; + } - // Stop the current command buffer. - encoder.close().map_pass_err(pass_scope)?; + unsafe { + state.pass.base.raw_encoder.end_compute_pass(); + } - // Create a new command buffer, which we will insert _before_ the body of the compute pass. - // - // Use that buffer to insert barriers and clear discarded images. - let transit = encoder - .open_pass(Some("(wgpu internal) Pre Pass")) - .map_pass_err(pass_scope)?; - fixup_discarded_surfaces( - pending_discard_init_fixups.into_iter(), - transit, - &mut tracker.textures, - device, - &snatch_guard, - ); - CommandBuffer::insert_barriers_from_tracker( - transit, - tracker, - &intermediate_trackers, - &snatch_guard, - ); - // Close the command buffer, and swap it with the previous. - encoder.close_and_swap().map_pass_err(pass_scope)?; + let State { + pass: pass::PassState { + pending_discard_init_fixups, + .. + }, + intermediate_trackers, + .. + } = state; + + // Stop the current command encoder. + parent_state.raw_encoder.close().map_pass_err(pass_scope)?; + + // Create a new command encoder, which we will insert _before_ the body of the compute pass. + // + // Use that buffer to insert barriers and clear discarded images. + let transit = parent_state + .raw_encoder + .open_pass(hal_label( + Some("(wgpu internal) Pre Pass"), + device.instance_flags, + )) + .map_pass_err(pass_scope)?; + fixup_discarded_surfaces( + pending_discard_init_fixups.into_iter(), + transit, + &mut parent_state.tracker.textures, + device, + parent_state.snatch_guard, + ); + CommandEncoder::insert_barriers_from_tracker( + transit, + parent_state.tracker, + &intermediate_trackers, + parent_state.snatch_guard, + ); + // Close the command encoder, and swap it with the previous. + parent_state + .raw_encoder + .close_and_swap() + .map_pass_err(pass_scope)?; - Ok(()) - }) - } + Ok(()) } fn set_pipeline( state: &mut State, - cmd_buf: &CommandBuffer, + device: &Arc, pipeline: Arc, ) -> Result<(), ComputePassErrorInner> { - pipeline.same_device_as(cmd_buf)?; + pipeline.same_device(device)?; state.pipeline = Some(pipeline.clone()); let pipeline = state - .general + .pass + .base .tracker .compute_pipelines .insert_single(pipeline) @@ -798,14 +821,15 @@ fn set_pipeline( unsafe { state - .general + .pass + .base .raw_encoder .set_compute_pipeline(pipeline.raw()); } // Rebind resources pass::rebind_resources::( - &mut state.general, + &mut state.pass, &pipeline.layout, &pipeline.late_sized_buffer_groups, || { @@ -834,7 +858,8 @@ fn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorI state.flush_states(None)?; let groups_size_limit = state - .general + .pass + .base .device .limits .max_compute_workgroups_per_dimension; @@ -852,28 +877,29 @@ fn dispatch(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorI } unsafe { - state.general.raw_encoder.dispatch(groups); + state.pass.base.raw_encoder.dispatch(groups); } Ok(()) } fn dispatch_indirect( state: &mut State, - cmd_buf: &CommandBuffer, + device: &Arc, buffer: Arc, offset: u64, ) -> Result<(), ComputePassErrorInner> { - buffer.same_device_as(cmd_buf)?; + buffer.same_device(device)?; state.is_ready()?; state - .general + .pass + .base .device .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?; buffer.check_usage(wgt::BufferUsages::INDIRECT)?; - buffer.check_destroyed(state.general.snatch_guard)?; + buffer.check_destroyed(state.pass.base.snatch_guard)?; if offset % 4 != 0 { return Err(ComputePassErrorInner::UnalignedIndirectBufferOffset(offset)); @@ -889,7 +915,7 @@ fn dispatch_indirect( } let stride = 3 * 4; // 3 integers, x/y/z group size - state.general.buffer_memory_init_actions.extend( + state.pass.base.buffer_memory_init_actions.extend( buffer.initialization_status.read().create_action( &buffer, offset..(offset + stride), @@ -897,21 +923,23 @@ fn dispatch_indirect( ), ); - if let Some(ref indirect_validation) = state.general.device.indirect_validation { - let params = - indirect_validation - .dispatch - .params(&state.general.device.limits, offset, buffer.size); + if let Some(ref indirect_validation) = state.pass.base.device.indirect_validation { + let params = indirect_validation.dispatch.params( + &state.pass.base.device.limits, + offset, + buffer.size, + ); unsafe { state - .general + .pass + .base .raw_encoder .set_compute_pipeline(params.pipeline); } unsafe { - state.general.raw_encoder.set_push_constants( + state.pass.base.raw_encoder.set_push_constants( params.pipeline_layout, wgt::ShaderStages::COMPUTE, 0, @@ -920,7 +948,7 @@ fn dispatch_indirect( } unsafe { - state.general.raw_encoder.set_bind_group( + state.pass.base.raw_encoder.set_bind_group( params.pipeline_layout, 0, Some(params.dst_bind_group), @@ -928,13 +956,13 @@ fn dispatch_indirect( ); } unsafe { - state.general.raw_encoder.set_bind_group( + state.pass.base.raw_encoder.set_bind_group( params.pipeline_layout, 1, Some( buffer .indirect_validation_bind_groups - .get(state.general.snatch_guard) + .get(state.pass.base.snatch_guard) .unwrap() .dispatch .as_ref(), @@ -948,17 +976,19 @@ fn dispatch_indirect( .buffers .set_single(&buffer, wgt::BufferUses::STORAGE_READ_ONLY); let src_barrier = src_transition - .map(|transition| transition.into_hal(&buffer, state.general.snatch_guard)); + .map(|transition| transition.into_hal(&buffer, state.pass.base.snatch_guard)); unsafe { state - .general + .pass + .base .raw_encoder .transition_buffers(src_barrier.as_slice()); } unsafe { state - .general + .pass + .base .raw_encoder .transition_buffers(&[hal::BufferBarrier { buffer: params.dst_buffer, @@ -970,7 +1000,7 @@ fn dispatch_indirect( } unsafe { - state.general.raw_encoder.dispatch([1, 1, 1]); + state.pass.base.raw_encoder.dispatch([1, 1, 1]); } // reset state @@ -979,14 +1009,15 @@ fn dispatch_indirect( unsafe { state - .general + .pass + .base .raw_encoder .set_compute_pipeline(pipeline.raw()); } if !state.push_constants.is_empty() { unsafe { - state.general.raw_encoder.set_push_constants( + state.pass.base.raw_encoder.set_push_constants( pipeline.layout.raw(), wgt::ShaderStages::COMPUTE, 0, @@ -995,11 +1026,11 @@ fn dispatch_indirect( } } - for (i, e) in state.general.binder.list_valid() { + for (i, e) in state.pass.binder.list_valid() { let group = e.group.as_ref().unwrap(); - let raw_bg = group.try_raw(state.general.snatch_guard)?; + let raw_bg = group.try_raw(state.pass.base.snatch_guard)?; unsafe { - state.general.raw_encoder.set_bind_group( + state.pass.base.raw_encoder.set_bind_group( pipeline.layout.raw(), i as u32, Some(raw_bg), @@ -1011,7 +1042,8 @@ fn dispatch_indirect( unsafe { state - .general + .pass + .base .raw_encoder .transition_buffers(&[hal::BufferBarrier { buffer: params.dst_buffer, @@ -1025,13 +1057,14 @@ fn dispatch_indirect( state.flush_states(None)?; unsafe { state - .general + .pass + .base .raw_encoder .dispatch_indirect(params.dst_buffer, 0); } } else { state - .general + .pass .scope .buffers .merge_single(&buffer, wgt::BufferUses::INDIRECT)?; @@ -1039,9 +1072,13 @@ fn dispatch_indirect( use crate::resource::Trackable; state.flush_states(Some(buffer.tracker_index()))?; - let buf_raw = buffer.try_raw(state.general.snatch_guard)?; + let buf_raw = buffer.try_raw(state.pass.base.snatch_guard)?; unsafe { - state.general.raw_encoder.dispatch_indirect(buf_raw, offset); + state + .pass + .base + .raw_encoder + .dispatch_indirect(buf_raw, offset); } } diff --git a/wgpu-core/src/command/draw.rs b/wgpu-core/src/command/draw.rs index 53a3f204fcc..7a57077a4fa 100644 --- a/wgpu-core/src/command/draw.rs +++ b/wgpu-core/src/command/draw.rs @@ -7,7 +7,7 @@ use wgt::error::{ErrorType, WebGpuError}; use super::bind::BinderError; use crate::command::pass; use crate::{ - binding_model::{LateMinBufferBindingSizeMismatch, PushConstantUploadError}, + binding_model::{BindingError, LateMinBufferBindingSizeMismatch, PushConstantUploadError}, resource::{ DestroyedResourceError, MissingBufferUsageError, MissingTextureUsageError, ResourceErrorIdent, @@ -56,6 +56,21 @@ pub enum DrawError { }, #[error(transparent)] BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch), + + #[error( + "Wrong pipeline type for this draw command. Attempted to call {} draw command on {} pipeline", + if *wanted_mesh_pipeline {"mesh shader"} else {"standard"}, + if *wanted_mesh_pipeline {"standard"} else {"mesh shader"}, + )] + WrongPipelineType { wanted_mesh_pipeline: bool }, + #[error( + "Each current draw group size dimension ({current:?}) must be less or equal to {limit}, and the product must be less or equal to {max_total}" + )] + InvalidGroupSize { + current: [u32; 3], + limit: u32, + max_total: u32, + }, } impl WebGpuError for DrawError { @@ -73,6 +88,12 @@ pub enum RenderCommandError { BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange), #[error("Vertex buffer index {index} is greater than the device's requested `max_vertex_buffers` limit {max}")] VertexBufferIndexOutOfRange { index: u32, max: u32 }, + #[error( + "Offset {offset} for vertex buffer in slot {slot} is not a multiple of `VERTEX_ALIGNMENT`" + )] + UnalignedVertexBuffer { slot: u32, offset: u64 }, + #[error("Offset {offset} for index buffer is not a multiple of {alignment}")] + UnalignedIndexBuffer { offset: u64, alignment: usize }, #[error("Render pipeline targets are incompatible with render pass")] IncompatiblePipelineTargets(#[from] crate::device::RenderPassCompatibilityError), #[error("{0} writes to depth, while the pass has read-only depth access")] @@ -89,6 +110,8 @@ pub enum RenderCommandError { MissingTextureUsage(#[from] MissingTextureUsageError), #[error(transparent)] PushConstants(#[from] PushConstantUploadError), + #[error(transparent)] + BindingError(#[from] BindingError), #[error("Viewport size {{ w: {w}, h: {h} }} greater than device's requested `max_texture_dimension_2d` limit {max}, or less than zero")] InvalidViewportRectSize { w: f32, h: f32, max: u32 }, #[error("Viewport has invalid rect {rect:?} for device's requested `max_texture_dimension_2d` limit; Origin less than -2 * `max_texture_dimension_2d` ({min}), or rect extends past 2 * `max_texture_dimension_2d` - 1 ({max})")] @@ -110,9 +133,12 @@ impl WebGpuError for RenderCommandError { Self::MissingBufferUsage(e) => e, Self::MissingTextureUsage(e) => e, Self::PushConstants(e) => e, + Self::BindingError(e) => e, Self::BindGroupIndexOutOfRange { .. } | Self::VertexBufferIndexOutOfRange { .. } + | Self::UnalignedIndexBuffer { .. } + | Self::UnalignedVertexBuffer { .. } | Self::IncompatibleDepthAccess(..) | Self::IncompatibleStencilAccess(..) | Self::InvalidViewportRectSize { .. } diff --git a/wgpu-core/src/command/encoder.rs b/wgpu-core/src/command/encoder.rs new file mode 100644 index 00000000000..19222c6e3ad --- /dev/null +++ b/wgpu-core/src/command/encoder.rs @@ -0,0 +1,53 @@ +use alloc::{sync::Arc, vec::Vec}; + +use crate::{ + command::memory_init::CommandBufferTextureMemoryActions, + device::{queue::TempResource, Device}, + init_tracker::BufferInitTrackerAction, + ray_tracing::AsAction, + snatch::SnatchGuard, + track::Tracker, +}; + +/// State applicable when encoding commands onto a compute pass, render pass, or +/// directly to a command encoder. +/// +/// Most encoding routines just want to receive an open encoder, write +/// command(s) to it, and leave it open for whatever is next. In this case the +/// `E` type parameter has the default value of `dyn hal::DynCommandEncoder`. To +/// avoid confusion about encoder state, we set the convention that _the encoder +/// in an `EncodingState` holding a bare HAL reference must always be open_. +/// +/// Compute and render passes are more complicated. Because they record a +/// command buffer for a housekeeping pre-pass which is inserted before the pass +/// itself, the first thing they will do is close and reopen the encoder if it +/// is already open. Unnecessary empty HAL passes can be avoided by passing them +/// the encoder in whatever state it happens to be. In this case, `E` is +/// `InnerCommandEncoder`, which tracks the state of the encoder. The callee +/// (the render or compute pass) will open and close the encoder as necessary. +/// +/// This structure is not supported by cbindgen because it contains a trait +/// object reference. +/// +/// cbindgen:ignore +pub(crate) struct EncodingState<'snatch_guard, 'cmd_enc, E: ?Sized = dyn hal::DynCommandEncoder> { + pub(crate) device: &'cmd_enc Arc, + + pub(crate) raw_encoder: &'cmd_enc mut E, + + pub(crate) tracker: &'cmd_enc mut Tracker, + pub(crate) buffer_memory_init_actions: &'cmd_enc mut Vec, + pub(crate) texture_memory_actions: &'cmd_enc mut CommandBufferTextureMemoryActions, + pub(crate) as_actions: &'cmd_enc mut Vec, + pub(crate) temp_resources: &'cmd_enc mut Vec, + pub(crate) indirect_draw_validation_resources: + &'cmd_enc mut crate::indirect_validation::DrawResources, + + pub(crate) snatch_guard: &'snatch_guard SnatchGuard<'snatch_guard>, + + /// Current debug scope nesting depth. + /// + /// When encoding a compute or render pass, this is the depth of debug + /// scopes in the pass, not the depth of debug scopes in the parent encoder. + pub(crate) debug_scope_depth: &'cmd_enc mut u32, +} diff --git a/wgpu-core/src/command/encoder_command.rs b/wgpu-core/src/command/encoder_command.rs new file mode 100644 index 00000000000..092228099bc --- /dev/null +++ b/wgpu-core/src/command/encoder_command.rs @@ -0,0 +1,141 @@ +use core::convert::Infallible; + +use alloc::{string::String, sync::Arc, vec::Vec}; + +use crate::{ + id, + resource::{Buffer, QuerySet, Texture}, +}; + +#[derive(Clone, Debug)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub enum Command { + CopyBufferToBuffer { + src: id::BufferId, + src_offset: wgt::BufferAddress, + dst: id::BufferId, + dst_offset: wgt::BufferAddress, + size: Option, + }, + CopyBufferToTexture { + src: wgt::TexelCopyBufferInfo, + dst: wgt::TexelCopyTextureInfo, + size: wgt::Extent3d, + }, + CopyTextureToBuffer { + src: wgt::TexelCopyTextureInfo, + dst: wgt::TexelCopyBufferInfo, + size: wgt::Extent3d, + }, + CopyTextureToTexture { + src: wgt::TexelCopyTextureInfo, + dst: wgt::TexelCopyTextureInfo, + size: wgt::Extent3d, + }, + ClearBuffer { + dst: id::BufferId, + offset: wgt::BufferAddress, + size: Option, + }, + ClearTexture { + dst: id::TextureId, + subresource_range: wgt::ImageSubresourceRange, + }, + WriteTimestamp { + query_set_id: id::QuerySetId, + query_index: u32, + }, + ResolveQuerySet { + query_set_id: id::QuerySetId, + start_query: u32, + query_count: u32, + destination: id::BufferId, + destination_offset: wgt::BufferAddress, + }, + PushDebugGroup(String), + PopDebugGroup, + InsertDebugMarker(String), + RunComputePass { + base: crate::command::BasePass, + timestamp_writes: Option, + }, + RunRenderPass { + base: crate::command::BasePass, + target_colors: Vec>, + target_depth_stencil: Option, + timestamp_writes: Option, + occlusion_query_set_id: Option, + }, + BuildAccelerationStructures { + blas: Vec, + tlas: Vec, + }, +} + +#[derive(Clone, Debug)] +pub enum ArcCommand { + CopyBufferToBuffer { + src: Arc, + src_offset: wgt::BufferAddress, + dst: Arc, + dst_offset: wgt::BufferAddress, + size: Option, + }, + CopyBufferToTexture { + src: wgt::TexelCopyBufferInfo>, + dst: wgt::TexelCopyTextureInfo>, + size: wgt::Extent3d, + }, + CopyTextureToBuffer { + src: wgt::TexelCopyTextureInfo>, + dst: wgt::TexelCopyBufferInfo>, + size: wgt::Extent3d, + }, + CopyTextureToTexture { + src: wgt::TexelCopyTextureInfo>, + dst: wgt::TexelCopyTextureInfo>, + size: wgt::Extent3d, + }, + ClearBuffer { + dst: Arc, + offset: wgt::BufferAddress, + size: Option, + }, + ClearTexture { + dst: Arc, + subresource_range: wgt::ImageSubresourceRange, + }, + WriteTimestamp { + query_set: Arc, + query_index: u32, + }, + ResolveQuerySet { + query_set: Arc, + start_query: u32, + query_count: u32, + destination: Arc, + destination_offset: wgt::BufferAddress, + }, + PushDebugGroup(String), + PopDebugGroup, + InsertDebugMarker(String), + RunComputePass { + pass: super::BasePass, + timestamp_writes: Option, + }, + RunRenderPass { + pass: super::BasePass, + color_attachments: super::ArcRenderPassColorAttachmentArray, + depth_stencil_attachment: Option, + timestamp_writes: Option, + occlusion_query_set: Option>, + }, + BuildAccelerationStructures { + blas: Vec, + tlas: Vec, + }, + TransitionResources { + buffer_transitions: Vec>>, + texture_transitions: Vec>>, + }, +} diff --git a/wgpu-core/src/command/ffi.rs b/wgpu-core/src/command/ffi.rs new file mode 100644 index 00000000000..9f53d7b5634 --- /dev/null +++ b/wgpu-core/src/command/ffi.rs @@ -0,0 +1,7 @@ +//! Types that are useful for FFI bindings to `wgpu`. + +use crate::id; + +pub type TexelCopyBufferInfo = wgt::TexelCopyBufferInfo; +pub type TexelCopyTextureInfo = wgt::TexelCopyTextureInfo; +pub type CopyExternalImageDestInfo = wgt::CopyExternalImageDestInfo; diff --git a/wgpu-core/src/command/memory_init.rs b/wgpu-core/src/command/memory_init.rs index bec16272374..faca417040e 100644 --- a/wgpu-core/src/command/memory_init.rs +++ b/wgpu-core/src/command/memory_init.rs @@ -15,7 +15,7 @@ use crate::{ FastHashMap, }; -use super::{clear::clear_texture, BakedCommands, ClearError}; +use super::{clear_texture, BakedCommands, ClearError}; /// Surface that was discarded by `StoreOp::Discard` of a preceding renderpass. /// Any read access to this surface needs to be preceded by a texture initialization. @@ -40,7 +40,7 @@ pub(crate) struct CommandBufferTextureMemoryActions { } impl CommandBufferTextureMemoryActions { - pub(crate) fn drain_init_actions(&mut self) -> Drain { + pub(crate) fn drain_init_actions(&mut self) -> Drain<'_, TextureInitTrackerAction> { self.init_actions.drain(..) } @@ -149,6 +149,7 @@ pub(crate) fn fixup_discarded_surfaces Locked(CommandBufferMutable), + Consumed, + /// Command recording is complete, and the buffer is ready for submission. /// /// [`Global::command_encoder_finish`] transitions a @@ -109,12 +148,21 @@ pub(crate) enum CommandEncoderStatus { } impl CommandEncoderStatus { - /// Record commands using the supplied closure. + #[cfg(feature = "trace")] + fn trace(&mut self) -> Option<&mut Vec> { + match self { + Self::Recording(cmd_buf_data) => cmd_buf_data.trace_commands.as_mut(), + _ => None, + } + } + + /// Push a command provided by a closure onto the encoder. /// /// If the encoder is in the [`Self::Recording`] state, calls the closure to - /// record commands. If the closure returns an error, stores that error in - /// the encoder for later reporting when `finish()` is called. Returns - /// `Ok(())` even if the closure returned an error. + /// obtain a command, and pushes it onto the encoder. If the closure returns + /// an error, stores that error in the encoder for later reporting when + /// `finish()` is called. Returns `Ok(())` even if the closure returned an + /// error. /// /// If the encoder is not in the [`Self::Recording`] state, the closure will /// not be called and nothing will be recorded. The encoder will be @@ -123,7 +171,51 @@ impl CommandEncoderStatus { /// returns `Ok(())`. /// /// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state - fn record_with< + fn push_with Result, E: Clone + Into>( + &mut self, + f: F, + ) -> Result<(), EncoderStateError> { + match self { + Self::Recording(cmd_buf_data) => { + match f() { + Ok(cmd) => cmd_buf_data.commands.push(cmd), + Err(err) => { + self.invalidate(err); + } + } + Ok(()) + } + Self::Locked(_) => { + // Invalidate the encoder and do not record anything, but do not + // return an immediate validation error. + self.invalidate(EncoderStateError::Locked); + Ok(()) + } + // Encoder is ended. Invalidate the encoder, do not record anything, + // and return an immediate validation error. + Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)), + Self::Consumed => Err(EncoderStateError::Ended), + // Encoder is already invalid. Do not record anything, but do not + // return an immediate validation error. + Self::Error(_) => Ok(()), + Self::Transitioning => unreachable!(), + } + } + + /// Call a closure with the inner command buffer structure. + /// + /// If the encoder is in the [`Self::Recording`] state, calls the provided + /// closure. If the closure returns an error, stores that error in the + /// encoder for later reporting when `finish()` is called. Returns `Ok(())` + /// even if the closure returned an error. + /// + /// If the encoder is not in the [`Self::Recording`] state, the closure will + /// not be called. The encoder will be invalidated (if it is not already). + /// If the error is a [validation error that should be raised + /// immediately][ves], returns it in `Err`, otherwise, returns `Ok(())`. + /// + /// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state + fn with_buffer< F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>, E: Clone + Into, >( @@ -144,6 +236,7 @@ impl CommandEncoderStatus { // Encoder is ended. Invalidate the encoder, do not record anything, // and return an immediate validation error. Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)), + Self::Consumed => Err(EncoderStateError::Ended), // Encoder is already invalid. Do not record anything, but do not // return an immediate validation error. Self::Error(_) => Ok(()), @@ -172,12 +265,13 @@ impl CommandEncoderStatus { self.invalidate(EncoderStateError::Ended); f(None) } + Self::Consumed => f(None), Self::Error(_) => f(None), Self::Transitioning => unreachable!(), } } - #[cfg(feature = "trace")] + #[cfg(all(feature = "trace", any(feature = "serde", feature = "replay")))] fn get_inner(&mut self) -> &mut CommandBufferMutable { match self { Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner, @@ -185,6 +279,7 @@ impl CommandEncoderStatus { // playing back a recorded trace. If only to avoid having to // implement serialization for all the error types, we don't support // storing the errors in a trace. + Self::Consumed => unreachable!("command encoder is consumed"), Self::Error(_) => unreachable!("passes in a trace do not store errors"), Self::Transitioning => unreachable!(), } @@ -193,7 +288,7 @@ impl CommandEncoderStatus { /// Locks the encoder by putting it in the [`Self::Locked`] state. /// /// Render or compute passes call this on start. At the end of the pass, - /// they call [`Self::unlock_and_record`] to put the [`CommandBuffer`] back + /// they call [`Self::unlock_encoder`] to put the [`CommandBuffer`] back /// into the [`Self::Recording`] state. fn lock_encoder(&mut self) -> Result<(), EncoderStateError> { match mem::replace(self, Self::Transitioning) { @@ -209,6 +304,10 @@ impl CommandEncoderStatus { Err(EncoderStateError::Ended) } Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)), + st @ Self::Consumed => { + *self = st; + Err(EncoderStateError::Ended) + } st @ Self::Error(_) => { *self = st; Err(EncoderStateError::Invalid) @@ -217,32 +316,19 @@ impl CommandEncoderStatus { } } - /// Unlocks the [`CommandBuffer`] and puts it back into the - /// [`Self::Recording`] state, then records commands using the supplied - /// closure. + /// Unlocks the encoder and puts it back into the [`Self::Recording`] state. /// /// This function is the unlocking counterpart to [`Self::lock_encoder`]. It /// is only valid to call this function if the encoder is in the /// [`Self::Locked`] state. /// - /// If the closure returns an error, stores that error in the encoder for - /// later reporting when `finish()` is called. Returns `Ok(())` even if the - /// closure returned an error. - /// - /// If the encoder is not in the [`Self::Locked`] state, the closure will - /// not be called and nothing will be recorded. If a validation error should - /// be raised immediately, returns it in `Err`, otherwise, returns `Ok(())`. - fn unlock_and_record< - F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>, - E: Clone + Into, - >( - &mut self, - f: F, - ) -> Result<(), EncoderStateError> { + /// If the encoder is in a state other than [`Self::Locked`] and a + /// validation error should be raised immediately, returns it in `Err`, + /// otherwise, stores the error in the encoder and returns `Ok(())`. + fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> { match mem::replace(self, Self::Transitioning) { Self::Locked(inner) => { *self = Self::Recording(inner); - RecordingGuard { inner: self }.record(f); Ok(()) } st @ Self::Finished(_) => { @@ -253,9 +339,13 @@ impl CommandEncoderStatus { *self = Self::Error(EncoderStateError::Unlocked.into()); Err(EncoderStateError::Unlocked) } + st @ Self::Consumed => { + *self = st; + Err(EncoderStateError::Ended) + } st @ Self::Error(_) => { - // Encoder is invalid. Do not record anything, but do not - // return an immediate validation error. + // Encoder is already invalid. The error will be reported by + // `CommandEncoder.finish`. *self = st; Ok(()) } @@ -263,21 +353,14 @@ impl CommandEncoderStatus { } } - fn finish(&mut self) -> Result<(), CommandEncoderError> { - match mem::replace(self, Self::Transitioning) { - Self::Recording(mut inner) => { - if let Err(e) = inner.encoder.close_if_open() { - Err(self.invalidate(e.into())) - } else { - *self = Self::Finished(inner); - // Note: if we want to stop tracking the swapchain texture view, - // this is the place to do it. - Ok(()) - } - } - Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended.into())), - Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked.into())), - Self::Error(err) => Err(self.invalidate(err)), + fn finish(&mut self) -> Self { + // Replace our state with `Consumed`, and return either the inner + // state or an error, to be transferred to the command buffer. + match mem::replace(self, Self::Consumed) { + Self::Recording(inner) => Self::Finished(inner), + Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()), + Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()), + st @ Self::Error(_) => st, Self::Transitioning => unreachable!(), } } @@ -288,7 +371,9 @@ impl CommandEncoderStatus { // Since we do not track the state of an invalid encoder, it is not // necessary to unlock an encoder that has been invalidated. fn invalidate>(&mut self, err: E) -> E { - *self = Self::Error(err.clone().into()); + let enc_err = err.clone().into(); + api_log!("Invalidating command encoder: {enc_err:?}"); + *self = Self::Error(enc_err); err } } @@ -371,6 +456,26 @@ impl<'a> ops::DerefMut for RecordingGuard<'a> { } } +pub(crate) struct CommandEncoder { + pub(crate) device: Arc, + + pub(crate) label: String, + + /// The mutable state of this command encoder. + pub(crate) data: Mutex, +} + +crate::impl_resource_type!(CommandEncoder); +crate::impl_labeled!(CommandEncoder); +crate::impl_parent_device!(CommandEncoder); +crate::impl_storage_item!(CommandEncoder); + +impl Drop for CommandEncoder { + fn drop(&mut self) { + resource_log!("Drop {}", self.error_ident()); + } +} + /// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it. /// /// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is @@ -382,16 +487,11 @@ impl<'a> ops::DerefMut for RecordingGuard<'a> { /// commands into the middle of a recorded stream. However, hal queue submission /// accepts a series of command buffers at once, so we can simply break the /// stream up into multiple buffers, and then reorder the buffers. See -/// [`CommandEncoder::close_and_swap`] for a specific example of this. -/// -/// Note that a [`CommandEncoderId`] actually refers to a [`CommandBuffer`]. -/// Methods that take a command encoder id actually look up the command buffer, -/// and then use its encoder. +/// [`InnerCommandEncoder::close_and_swap`] for a specific example of this. /// /// [rce]: hal::Api::CommandEncoder /// [rcb]: hal::Api::CommandBuffer -/// [`CommandEncoderId`]: crate::id::CommandEncoderId -pub(crate) struct CommandEncoder { +pub(crate) struct InnerCommandEncoder { /// The underlying `wgpu_hal` [`CommandEncoder`]. /// /// Successfully executed command buffers' encoders are saved in a @@ -424,10 +524,10 @@ pub(crate) struct CommandEncoder { /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder pub(crate) is_open: bool, - pub(crate) hal_label: Option, + pub(crate) label: String, } -impl CommandEncoder { +impl InnerCommandEncoder { /// Finish the current command buffer and insert it just before /// the last element in [`self.list`][l]. /// @@ -450,7 +550,7 @@ impl CommandEncoder { /// /// - If the encoder is not open. /// - /// [l]: CommandEncoder::list + /// [l]: InnerCommandEncoder::list /// [`transition_buffers`]: hal::CommandEncoder::transition_buffers /// [`transition_textures`]: hal::CommandEncoder::transition_textures fn close_and_swap(&mut self) -> Result<(), DeviceError> { @@ -473,7 +573,7 @@ impl CommandEncoder { /// /// - If the encoder is not open. /// - /// [l]: CommandEncoder::list + /// [l]: InnerCommandEncoder::list pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> { assert!(self.is_open); self.is_open = false; @@ -494,7 +594,7 @@ impl CommandEncoder { /// /// - If the encoder is not open. /// - /// [l]: CommandEncoder::list + /// [l]: InnerCommandEncoder::list pub(crate) fn close(&mut self) -> Result<(), DeviceError> { assert!(self.is_open); self.is_open = false; @@ -515,7 +615,7 @@ impl CommandEncoder { /// /// On return, the underlying hal encoder is closed. /// - /// [l]: CommandEncoder::list + /// [l]: InnerCommandEncoder::list fn close_if_open(&mut self) -> Result<(), DeviceError> { if self.is_open { self.is_open = false; @@ -527,13 +627,29 @@ impl CommandEncoder { Ok(()) } + /// If the command encoder is not open, begin recording a new command buffer. + /// + /// If the command encoder was already open, does nothing. + /// + /// In both cases, returns a reference to the raw encoder. + fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> { + if !self.is_open { + self.is_open = true; + let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags); + unsafe { self.raw.begin_encoding(hal_label) } + .map_err(|e| self.device.handle_hal_error(e))?; + } + + Ok(self.raw.as_mut()) + } + /// Begin recording a new command buffer, if we haven't already. /// /// The underlying hal encoder is put in the "recording" state. pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> { if !self.is_open { self.is_open = true; - let hal_label = self.hal_label.as_deref(); + let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags); unsafe { self.raw.begin_encoding(hal_label) } .map_err(|e| self.device.handle_hal_error(e))?; } @@ -541,7 +657,7 @@ impl CommandEncoder { Ok(self.raw.as_mut()) } - /// Begin recording a new command buffer for a render pass, with + /// Begin recording a new command buffer for a render or compute pass, with /// its own label. /// /// The underlying hal encoder is put in the "recording" state. @@ -564,7 +680,7 @@ impl CommandEncoder { } } -impl Drop for CommandEncoder { +impl Drop for InnerCommandEncoder { fn drop(&mut self) { if self.is_open { unsafe { self.raw.discard_encoding() }; @@ -581,7 +697,7 @@ impl Drop for CommandEncoder { /// Look at the documentation for [`CommandBufferMutable`] for an explanation of /// the fields in this struct. This is the "built" counterpart to that type. pub(crate) struct BakedCommands { - pub(crate) encoder: CommandEncoder, + pub(crate) encoder: InnerCommandEncoder, pub(crate) trackers: Tracker, pub(crate) temp_resources: Vec, pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources, @@ -595,7 +711,7 @@ pub struct CommandBufferMutable { /// they belong to. /// /// [`wgpu_hal::Api::CommandBuffer`]: hal::Api::CommandBuffer - pub(crate) encoder: CommandEncoder, + pub(crate) encoder: InnerCommandEncoder, /// All the resources that the commands recorded so far have referred to. pub(crate) trackers: Tracker, @@ -609,27 +725,18 @@ pub struct CommandBufferMutable { buffer_memory_init_actions: Vec, texture_memory_actions: CommandBufferTextureMemoryActions, - pub(crate) pending_query_resets: QueryResetMap, - as_actions: Vec, temp_resources: Vec, indirect_draw_validation_resources: crate::indirect_validation::DrawResources, + pub(crate) commands: Vec, + #[cfg(feature = "trace")] - pub(crate) commands: Option>, + pub(crate) trace_commands: Option>, } impl CommandBufferMutable { - pub(crate) fn open_encoder_and_tracker( - &mut self, - ) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> { - let encoder = self.encoder.open()?; - let tracker = &mut self.trackers; - - Ok((encoder, tracker)) - } - pub(crate) fn into_baked_commands(self) -> BakedCommands { BakedCommands { encoder: self.encoder, @@ -644,25 +751,11 @@ impl CommandBufferMutable { /// A buffer of commands to be submitted to the GPU for execution. /// -/// Whereas the WebGPU API uses two separate types for command buffers and -/// encoders, this type is a fusion of the two: -/// -/// - During command recording, this holds a [`CommandEncoder`] accepting this -/// buffer's commands. In this state, the [`CommandBuffer`] type behaves like -/// a WebGPU `GPUCommandEncoder`. -/// -/// - Once command recording is finished by calling -/// [`Global::command_encoder_finish`], no further recording is allowed. The -/// internal [`CommandEncoder`] is retained solely as a storage pool for the -/// raw command buffers. In this state, the value behaves like a WebGPU -/// `GPUCommandBuffer`. -/// -/// - Once a command buffer is submitted to the queue, it is removed from the id -/// registry, and its contents are taken to construct a [`BakedCommands`], -/// whose contents eventually become the property of the submission queue. +/// Once a command buffer is submitted to the queue, its contents are taken +/// to construct a [`BakedCommands`], whose contents eventually become the +/// property of the submission queue. pub struct CommandBuffer { pub(crate) device: Arc, - support_clear_texture: bool, /// The `label` from the descriptor used to create the resource. label: String, @@ -676,36 +769,35 @@ impl Drop for CommandBuffer { } } -impl CommandBuffer { +impl CommandEncoder { pub(crate) fn new( encoder: Box, device: &Arc, label: &Label, ) -> Self { - CommandBuffer { + CommandEncoder { device: device.clone(), - support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE), label: label.to_string(), data: Mutex::new( rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Recording(CommandBufferMutable { - encoder: CommandEncoder { + encoder: InnerCommandEncoder { raw: ManuallyDrop::new(encoder), list: Vec::new(), device: device.clone(), is_open: false, - hal_label: label.to_hal(device.instance_flags).map(str::to_owned), + label: label.to_string(), }, trackers: Tracker::new(), buffer_memory_init_actions: Default::default(), texture_memory_actions: Default::default(), - pending_query_resets: QueryResetMap::new(), as_actions: Default::default(), temp_resources: Default::default(), indirect_draw_validation_resources: crate::indirect_validation::DrawResources::new(device.clone()), + commands: Vec::new(), #[cfg(feature = "trace")] - commands: if device.trace.lock().is_some() { + trace_commands: if device.trace.lock().is_some() { Some(Vec::new()) } else { None @@ -720,9 +812,8 @@ impl CommandBuffer { label: &Label, err: CommandEncoderError, ) -> Self { - CommandBuffer { + CommandEncoder { device: device.clone(), - support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE), label: label.to_string(), data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)), } @@ -814,10 +905,7 @@ impl CommandBuffer { ) { St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable), St::Error(err) => Err(err), - St::Recording(_) | St::Locked(_) => { - Err(InvalidResourceError(self.error_ident()).into()) - } - St::Transitioning => unreachable!(), + St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(), } } } @@ -855,6 +943,10 @@ pub struct BasePass { pub error: Option, /// The stream of commands. + /// + /// The commands are moved out of this vector when the pass is ended (i.e. + /// at the same time that `parent` is taken out of the + /// `ComputePass`/`RenderPass`). pub commands: Vec, /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`. @@ -898,6 +990,26 @@ impl BasePass { push_constant_data: Vec::new(), } } + + /// Takes the commands from the pass, or returns an error if the pass is + /// invalid. + /// + /// This is called when the pass is ended, at the same time that the + /// `parent` member of the `ComputePass` or `RenderPass` containing the pass + /// is taken. + fn take(&mut self) -> Result, E> { + match self.error.as_ref() { + Some(err) => Err(err.clone()), + None => Ok(BasePass { + label: self.label.clone(), + error: None, + commands: mem::take(&mut self.commands), + dynamic_offsets: mem::take(&mut self.dynamic_offsets), + string_data: mem::take(&mut self.string_data), + push_constant_data: mem::take(&mut self.push_constant_data), + }), + } + } } /// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and @@ -1028,6 +1140,8 @@ pub enum CommandEncoderError { #[error(transparent)] ResourceUsage(#[from] ResourceUsageCompatibilityError), #[error(transparent)] + DebugGroupError(#[from] DebugGroupError), + #[error(transparent)] MissingFeatures(#[from] MissingFeatures), #[error(transparent)] Transfer(#[from] TransferError), @@ -1060,6 +1174,18 @@ impl CommandEncoderError { inner: RenderPassErrorInner::DestroyedResource(_), .. }) + | Self::RenderPass(RenderPassError { + inner: RenderPassErrorInner::RenderCommand( + RenderCommandError::DestroyedResource(_) + ), + .. + }) + | Self::RenderPass(RenderPassError { + inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError( + BindingError::DestroyedResource(_) + )), + .. + }) ) } } @@ -1069,6 +1195,7 @@ impl WebGpuError for CommandEncoderError { let e: &dyn WebGpuError = match self { Self::Device(e) => e, Self::InvalidResource(e) => e, + Self::DebugGroupError(e) => e, Self::MissingFeatures(e) => e, Self::State(e) => e, Self::DestroyedResource(e) => e, @@ -1085,6 +1212,23 @@ impl WebGpuError for CommandEncoderError { } } +#[derive(Clone, Debug, Error)] +#[non_exhaustive] +pub enum DebugGroupError { + #[error("Cannot pop debug group, because number of pushed debug groups is zero")] + InvalidPop, + #[error("A debug group was not popped before the encoder was finished")] + MissingPop, +} + +impl WebGpuError for DebugGroupError { + fn webgpu_error_type(&self) -> ErrorType { + match self { + Self::InvalidPop | Self::MissingPop => ErrorType::Validation, + } + } +} + #[derive(Clone, Debug, Error)] #[non_exhaustive] pub enum TimestampWritesError { @@ -1105,25 +1249,239 @@ impl WebGpuError for TimestampWritesError { } impl Global { + fn resolve_buffer_id( + &self, + buffer_id: Id, + ) -> Result, InvalidResourceError> { + self.hub.buffers.get(buffer_id).get() + } + + fn resolve_texture_id( + &self, + texture_id: Id, + ) -> Result, InvalidResourceError> { + self.hub.textures.get(texture_id).get() + } + + fn resolve_query_set( + &self, + query_set_id: Id, + ) -> Result, InvalidResourceError> { + self.hub.query_sets.get(query_set_id).get() + } + pub fn command_encoder_finish( &self, encoder_id: id::CommandEncoderId, - _desc: &wgt::CommandBufferDescriptor